QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
qgsrasterchecker.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsrasterchecker.cpp
3 --------------------------------------
4 Date : 5 Sep 2012
5 Copyright : (C) 2012 by Radim Blazek
6 Email : radim dot blazek at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16#include "qgsproviderregistry.h"
17#include "qgsrasterchecker.h"
19#include "qgsrasterlayer.h"
20
21#include <QColor>
22#include <QPainter>
23#include <QImage>
24#include <QTime>
25#include <QCryptographicHash>
26#include <QByteArray>
27#include <QDebug>
28#include <QBuffer>
29
31{
32 mTabStyle = QStringLiteral( "border-spacing: 0px; border-width: 1px 1px 0 0; border-style: solid;" );
33 mCellStyle = QStringLiteral( "border-width: 0 0 1px 1px; border-style: solid; font-size: smaller; text-align: center;" );
34 mOkStyle = QStringLiteral( "background: #00ff00;" );
35 mErrStyle = QStringLiteral( "background: #ff0000;" );
36 mErrMsgStyle = QStringLiteral( "color: #ff0000;" );
37}
38
39bool QgsRasterChecker::runTest( const QString &verifiedKey, QString verifiedUri,
40 const QString &expectedKey, QString expectedUri )
41{
42 bool ok = true;
43 mReport += QLatin1String( "\n\n" );
44
45 //QgsRasterDataProvider* verifiedProvider = QgsRasterLayer::loadProvider( verifiedKey, verifiedUri );
47 QgsRasterDataProvider *verifiedProvider = qobject_cast< QgsRasterDataProvider * >( QgsProviderRegistry::instance()->createProvider( verifiedKey, verifiedUri, options ) );
48 if ( !verifiedProvider || !verifiedProvider->isValid() )
49 {
50 error( QStringLiteral( "Cannot load provider %1 with URI: %2" ).arg( verifiedKey, verifiedUri ), mReport );
51 return false;
52 }
53
54 //QgsRasterDataProvider* expectedProvider = QgsRasterLayer::loadProvider( expectedKey, expectedUri );
55 QgsRasterDataProvider *expectedProvider = qobject_cast< QgsRasterDataProvider * >( QgsProviderRegistry::instance()->createProvider( expectedKey, expectedUri, options ) );
56 if ( !expectedProvider || !expectedProvider->isValid() )
57 {
58 error( QStringLiteral( "Cannot load provider %1 with URI: %2" ).arg( expectedKey, expectedUri ), mReport );
59 return false;
60 }
61
62 mReport += QStringLiteral( "Verified URI: %1<br>" ).arg( verifiedUri.replace( '&', QLatin1String( "&amp;" ) ) );
63 mReport += QStringLiteral( "Expected URI: %1<br>" ).arg( expectedUri.replace( '&', QLatin1String( "&amp;" ) ) );
64
65 mReport += QLatin1String( "<br>" );
66 mReport += QStringLiteral( "<table style='%1'>\n" ).arg( mTabStyle );
67 mReport += compareHead();
68
69 compare( QStringLiteral( "Band count" ), verifiedProvider->bandCount(), expectedProvider->bandCount(), mReport, ok );
70
71 compare( QStringLiteral( "Width" ), verifiedProvider->xSize(), expectedProvider->xSize(), mReport, ok );
72 compare( QStringLiteral( "Height" ), verifiedProvider->ySize(), expectedProvider->ySize(), mReport, ok );
73
74 compareRow( QStringLiteral( "Extent" ), verifiedProvider->extent().toString(), expectedProvider->extent().toString(), mReport, verifiedProvider->extent() == expectedProvider->extent() );
75
76 if ( verifiedProvider->extent() != expectedProvider->extent() ) ok = false;
77
78
79 mReport += QLatin1String( "</table>\n" );
80
81 if ( !ok ) return false;
82
83 bool allOk = true;
84 for ( int band = 1; band <= expectedProvider->bandCount(); band++ )
85 {
86 mReport += QStringLiteral( "<h3>Band %1</h3>\n" ).arg( band );
87 mReport += QStringLiteral( "<table style='%1'>\n" ).arg( mTabStyle );
88 mReport += compareHead();
89
90 // Data types may differ (?)
91 bool typesOk = true;
92 compare( QStringLiteral( "Source data type" ), verifiedProvider->sourceDataType( band ), expectedProvider->sourceDataType( band ), mReport, typesOk );
93 compare( QStringLiteral( "Data type" ), verifiedProvider->dataType( band ), expectedProvider->dataType( band ), mReport, typesOk );
94
95 // Check nodata
96 bool noDataOk = true;
97 compare( QStringLiteral( "No data (NULL) value existence flag" ), verifiedProvider->sourceHasNoDataValue( band ), expectedProvider->sourceHasNoDataValue( band ), mReport, noDataOk );
98 if ( verifiedProvider->sourceHasNoDataValue( band ) && expectedProvider->sourceHasNoDataValue( band ) )
99 {
100 compare( QStringLiteral( "No data (NULL) value" ), verifiedProvider->sourceNoDataValue( band ), expectedProvider->sourceNoDataValue( band ), mReport, noDataOk );
101 }
102
103 bool statsOk = true;
104 const QgsRasterBandStats verifiedStats = verifiedProvider->bandStatistics( band );
105 const QgsRasterBandStats expectedStats = expectedProvider->bandStatistics( band );
106
107 // Min/max may 'slightly' differ, for big numbers however, the difference may
108 // be quite big, for example for Float32 with max -3.332e+38, the difference is 1.47338e+24
109 double tol = tolerance( expectedStats.minimumValue );
110 compare( QStringLiteral( "Minimum value" ), verifiedStats.minimumValue, expectedStats.minimumValue, mReport, statsOk, tol );
111 tol = tolerance( expectedStats.maximumValue );
112 compare( QStringLiteral( "Maximum value" ), verifiedStats.maximumValue, expectedStats.maximumValue, mReport, statsOk, tol );
113
114 // TODO: enable once fixed (WCS excludes nulls but GDAL does not)
115 //compare( "Cells count", verifiedStats.elementCount, expectedStats.elementCount, mReport, statsOk );
116
117 tol = tolerance( expectedStats.mean );
118 compare( QStringLiteral( "Mean" ), verifiedStats.mean, expectedStats.mean, mReport, statsOk, tol );
119
120 // stdDev usually differ significantly
121 tol = tolerance( expectedStats.stdDev, 1 );
122 compare( QStringLiteral( "Standard deviation" ), verifiedStats.stdDev, expectedStats.stdDev, mReport, statsOk, tol );
123
124 mReport += QLatin1String( "</table>" );
125 mReport += QLatin1String( "<br>" );
126
127 if ( !statsOk || !typesOk || !noDataOk )
128 {
129 allOk = false;
130 // create values table anyway so that values are available
131 }
132
133 mReport += QLatin1String( "<table><tr>" );
134 mReport += QLatin1String( "<td>Data comparison</td>" );
135 mReport += QStringLiteral( "<td style='%1 %2 border: 1px solid'>correct&nbsp;value</td>" ).arg( mCellStyle, mOkStyle );
136 mReport += QLatin1String( "<td></td>" );
137 mReport += QStringLiteral( "<td style='%1 %2 border: 1px solid'>wrong&nbsp;value<br>expected value</td></tr>" ).arg( mCellStyle, mErrStyle );
138 mReport += QLatin1String( "</tr></table>" );
139 mReport += QLatin1String( "<br>" );
140
141 const int width = expectedProvider->xSize();
142 const int height = expectedProvider->ySize();
143 std::unique_ptr< QgsRasterBlock > expectedBlock( expectedProvider->block( band, expectedProvider->extent(), width, height ) );
144 std::unique_ptr< QgsRasterBlock > verifiedBlock( verifiedProvider->block( band, expectedProvider->extent(), width, height ) );
145
146 if ( !expectedBlock || !expectedBlock->isValid() ||
147 !verifiedBlock || !verifiedBlock->isValid() )
148 {
149 allOk = false;
150 mReport += QLatin1String( "cannot read raster block" );
151 continue;
152 }
153
154 // compare data values
155 QString htmlTable = QStringLiteral( "<table style='%1'>" ).arg( mTabStyle );
156 for ( int row = 0; row < height; row ++ )
157 {
158 htmlTable += QLatin1String( "<tr>" );
159 for ( int col = 0; col < width; col ++ )
160 {
161 bool cellOk = true;
162 const double verifiedVal = verifiedBlock->value( row, col );
163 const double expectedVal = expectedBlock->value( row, col );
164
165 QString valStr;
166 if ( compare( verifiedVal, expectedVal, 0 ) )
167 {
168 valStr = QString::number( verifiedVal );
169 }
170 else
171 {
172 cellOk = false;
173 allOk = false;
174 valStr = QStringLiteral( "%1<br>%2" ).arg( verifiedVal ).arg( expectedVal );
175 }
176 htmlTable += QStringLiteral( "<td style='%1 %2'>%3</td>" ).arg( mCellStyle, cellOk ? mOkStyle : mErrStyle, valStr );
177 }
178 htmlTable += QLatin1String( "</tr>" );
179 }
180 htmlTable += QLatin1String( "</table>" );
181
182 mReport += htmlTable;
183 }
184 delete verifiedProvider;
185 delete expectedProvider;
186 return allOk;
187}
188
189void QgsRasterChecker::error( const QString &message, QString &report )
190{
191 report += QStringLiteral( "<font style='%1'>Error: " ).arg( mErrMsgStyle );
192 report += message;
193 report += QLatin1String( "</font>" );
194}
195
196double QgsRasterChecker::tolerance( double val, int places )
197{
198 // float precision is about 7 decimal digits, double about 16
199 // default places = 6
200 return 1. * std::pow( 10, std::round( std::log10( std::fabs( val ) ) - places ) );
201}
202
203QString QgsRasterChecker::compareHead()
204{
205 QString html;
206 html += QStringLiteral( "<tr><th style='%1'>Param name</th><th style='%1'>Verified value</th><th style='%1'>Expected value</th><th style='%1'>Difference</th><th style='%1'>Tolerance</th></tr>" ).arg( mCellStyle );
207 return html;
208}
209
210void QgsRasterChecker::compare( const QString &paramName, int verifiedVal, int expectedVal, QString &report, bool &ok )
211{
212 const bool isEqual = verifiedVal == expectedVal;
213 compareRow( paramName, QString::number( verifiedVal ), QString::number( expectedVal ), report, isEqual, QString::number( verifiedVal - expectedVal ) );
214 if ( !isEqual )
215 ok = false;
216}
217
218void QgsRasterChecker::compare( const QString &paramName, Qgis::DataType verifiedVal, Qgis::DataType expectedVal, QString &report, bool &ok )
219{
220 const bool isEqual = verifiedVal == expectedVal;
221 compareRow( paramName, QString::number( static_cast< int>( verifiedVal ) ), QString::number( static_cast< int >( expectedVal ) ), report, isEqual, QString::number( static_cast< int >( verifiedVal ) - static_cast< int>( expectedVal ) ) );
222 if ( !isEqual )
223 ok = false;
224}
225
226bool QgsRasterChecker::compare( double verifiedVal, double expectedVal, double tolerance )
227{
228 // values may be nan
229 return ( std::isnan( verifiedVal ) && std::isnan( expectedVal ) ) || ( std::fabs( verifiedVal - expectedVal ) <= tolerance );
230}
231
232void QgsRasterChecker::compare( const QString &paramName, double verifiedVal, double expectedVal, QString &report, bool &ok, double tolerance )
233{
234 const bool isNearEqual = compare( verifiedVal, expectedVal, tolerance );
235 compareRow( paramName, QString::number( verifiedVal ), QString::number( expectedVal ), report, isNearEqual, QString::number( verifiedVal - expectedVal ), QString::number( tolerance ) );
236 if ( !isNearEqual )
237 ok = false;
238}
239
240void QgsRasterChecker::compareRow( const QString &paramName, const QString &verifiedVal, const QString &expectedVal, QString &report, bool ok, const QString &difference, const QString &tolerance )
241{
242 report += QLatin1String( "<tr>\n" );
243 report += QStringLiteral( "<td style='%1'>%2</td><td style='%1 %3'>%4</td><td style='%1'>%5</td>\n" ).arg( mCellStyle, paramName, ok ? mOkStyle : mErrStyle, verifiedVal, expectedVal );
244 report += QStringLiteral( "<td style='%1'>%2</td>\n" ).arg( mCellStyle, difference );
245 report += QStringLiteral( "<td style='%1'>%2</td>\n" ).arg( mCellStyle, tolerance );
246 report += QLatin1String( "</tr>" );
247}
DataType
Raster data types.
Definition: qgis.h:269
virtual bool isValid() const =0
Returns true if this is a valid layer.
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
The RasterBandStats struct is a container for statistics about a single raster band.
double mean
The mean cell value for the band. NO_DATA values are excluded.
double stdDev
The standard deviation of the cell values.
double minimumValue
The minimum cell value in the raster band.
double maximumValue
The maximum cell value in the raster band.
bool runTest(const QString &verifiedKey, QString verifiedUri, const QString &expectedKey, QString expectedUri)
Test using renderer to generate the image to be compared.
QString report() const
Base class for raster data providers.
virtual bool sourceHasNoDataValue(int bandNo) const
Returns true if source band has no data value.
Qgis::DataType sourceDataType(int bandNo) const override=0
Returns source data type for the band specified by number, source data type may be shorter than dataT...
virtual double sourceNoDataValue(int bandNo) const
Value representing no data value.
QgsRectangle extent() const override=0
Returns the extent of the layer.
Qgis::DataType dataType(int bandNo) const override=0
Returns data type for the band specified by number.
QgsRasterBlock * block(int bandNo, const QgsRectangle &boundingBox, int width, int height, QgsRasterBlockFeedback *feedback=nullptr) override
Read block of data using given extent and size.
virtual int xSize() const
Gets raster size.
Q_DECL_DEPRECATED QgsRasterBandStats bandStatistics(int bandNo, int stats, const QgsRectangle &extent=QgsRectangle(), int sampleSize=0, QgsRasterBlockFeedback *feedback=nullptr)
Returns the band statistics.
virtual int bandCount() const =0
Gets number of bands.
virtual int ySize() const
QString toString(int precision=16) const
Returns a string representation of form xmin,ymin : xmax,ymax Coordinates will be truncated to the sp...
Setting options for creating vector data providers.