QGIS API Documentation 3.99.0-Master (2fe06baccd8)
Loading...
Searching...
No Matches
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 "qgsrasterchecker.h"
17
18#include "qgsproviderregistry.h"
20#include "qgsrasterlayer.h"
21
22#include <QBuffer>
23#include <QByteArray>
24#include <QColor>
25#include <QCryptographicHash>
26#include <QDebug>
27#include <QImage>
28#include <QPainter>
29#include <QTime>
30
32{
33 mTabStyle = QStringLiteral( "border-spacing: 0px; border-width: 1px 1px 0 0; border-style: solid;" );
34 mCellStyle = QStringLiteral( "border-width: 0 0 1px 1px; border-style: solid; font-size: smaller; text-align: center;" );
35 mOkStyle = QStringLiteral( "background: #00ff00;" );
36 mErrStyle = QStringLiteral( "background: #ff0000;" );
37 mErrMsgStyle = QStringLiteral( "color: #ff0000;" );
38}
39
40bool QgsRasterChecker::runTest( const QString &verifiedKey, QString verifiedUri,
41 const QString &expectedKey, QString expectedUri )
42{
43 bool ok = true;
44 mReport += QLatin1String( "\n\n" );
45
46 //QgsRasterDataProvider* verifiedProvider = QgsRasterLayer::loadProvider( verifiedKey, verifiedUri );
48 QgsRasterDataProvider *verifiedProvider = qobject_cast< QgsRasterDataProvider * >( QgsProviderRegistry::instance()->createProvider( verifiedKey, verifiedUri, options ) );
49 if ( !verifiedProvider || !verifiedProvider->isValid() )
50 {
51 error( QStringLiteral( "Cannot load provider %1 with URI: %2" ).arg( verifiedKey, verifiedUri ), mReport );
52 return false;
53 }
54
55 //QgsRasterDataProvider* expectedProvider = QgsRasterLayer::loadProvider( expectedKey, expectedUri );
56 QgsRasterDataProvider *expectedProvider = qobject_cast< QgsRasterDataProvider * >( QgsProviderRegistry::instance()->createProvider( expectedKey, expectedUri, options ) );
57 if ( !expectedProvider || !expectedProvider->isValid() )
58 {
59 error( QStringLiteral( "Cannot load provider %1 with URI: %2" ).arg( expectedKey, expectedUri ), mReport );
60 return false;
61 }
62
63 mReport += QStringLiteral( "Verified URI: %1<br>" ).arg( verifiedUri.replace( '&', QLatin1String( "&amp;" ) ) );
64 mReport += QStringLiteral( "Expected URI: %1<br>" ).arg( expectedUri.replace( '&', QLatin1String( "&amp;" ) ) );
65
66 mReport += QLatin1String( "<br>" );
67 mReport += QStringLiteral( "<table style='%1'>\n" ).arg( mTabStyle );
68 mReport += compareHead();
69
70 compare( QStringLiteral( "Band count" ), verifiedProvider->bandCount(), expectedProvider->bandCount(), mReport, ok );
71
72 compare( QStringLiteral( "Width" ), verifiedProvider->xSize(), expectedProvider->xSize(), mReport, ok );
73 compare( QStringLiteral( "Height" ), verifiedProvider->ySize(), expectedProvider->ySize(), mReport, ok );
74
75 compareRow( QStringLiteral( "Extent" ), verifiedProvider->extent().toString(), expectedProvider->extent().toString(), mReport, verifiedProvider->extent() == expectedProvider->extent() );
76
77 if ( verifiedProvider->extent() != expectedProvider->extent() ) ok = false;
78
79
80 mReport += QLatin1String( "</table>\n" );
81
82 if ( !ok ) return false;
83
84 bool allOk = true;
85 for ( int band = 1; band <= expectedProvider->bandCount(); band++ )
86 {
87 mReport += QStringLiteral( "<h3>Band %1</h3>\n" ).arg( band );
88 mReport += QStringLiteral( "<table style='%1'>\n" ).arg( mTabStyle );
89 mReport += compareHead();
90
91 // Data types may differ (?)
92 bool typesOk = true;
93 compare( QStringLiteral( "Source data type" ), verifiedProvider->sourceDataType( band ), expectedProvider->sourceDataType( band ), mReport, typesOk );
94 compare( QStringLiteral( "Data type" ), verifiedProvider->dataType( band ), expectedProvider->dataType( band ), mReport, typesOk );
95
96 // Check nodata
97 bool noDataOk = true;
98 compare( QStringLiteral( "No data (NULL) value existence flag" ), verifiedProvider->sourceHasNoDataValue( band ), expectedProvider->sourceHasNoDataValue( band ), mReport, noDataOk );
99 if ( verifiedProvider->sourceHasNoDataValue( band ) && expectedProvider->sourceHasNoDataValue( band ) )
100 {
101 compare( QStringLiteral( "No data (NULL) value" ), verifiedProvider->sourceNoDataValue( band ), expectedProvider->sourceNoDataValue( band ), mReport, noDataOk );
102 }
103
104 bool statsOk = true;
105 const QgsRasterBandStats verifiedStats = verifiedProvider->bandStatistics( band );
106 const QgsRasterBandStats expectedStats = expectedProvider->bandStatistics( band );
107
108 // Min/max may 'slightly' differ, for big numbers however, the difference may
109 // be quite big, for example for Float32 with max -3.332e+38, the difference is 1.47338e+24
110 double tol = tolerance( expectedStats.minimumValue );
111 compare( QStringLiteral( "Minimum value" ), verifiedStats.minimumValue, expectedStats.minimumValue, mReport, statsOk, tol );
112 tol = tolerance( expectedStats.maximumValue );
113 compare( QStringLiteral( "Maximum value" ), verifiedStats.maximumValue, expectedStats.maximumValue, mReport, statsOk, tol );
114
115 // TODO: enable once fixed (WCS excludes nulls but GDAL does not)
116 //compare( "Cells count", verifiedStats.elementCount, expectedStats.elementCount, mReport, statsOk );
117
118 tol = tolerance( expectedStats.mean );
119 compare( QStringLiteral( "Mean" ), verifiedStats.mean, expectedStats.mean, mReport, statsOk, tol );
120
121 // stdDev usually differ significantly
122 tol = tolerance( expectedStats.stdDev, 1 );
123 compare( QStringLiteral( "Standard deviation" ), verifiedStats.stdDev, expectedStats.stdDev, mReport, statsOk, tol );
124
125 mReport += QLatin1String( "</table>" );
126 mReport += QLatin1String( "<br>" );
127
128 if ( !statsOk || !typesOk || !noDataOk )
129 {
130 allOk = false;
131 // create values table anyway so that values are available
132 }
133
134 mReport += QLatin1String( "<table><tr>" );
135 mReport += QLatin1String( "<td>Data comparison</td>" );
136 mReport += QStringLiteral( "<td style='%1 %2 border: 1px solid'>correct&nbsp;value</td>" ).arg( mCellStyle, mOkStyle );
137 mReport += QLatin1String( "<td></td>" );
138 mReport += QStringLiteral( "<td style='%1 %2 border: 1px solid'>wrong&nbsp;value<br>expected value</td></tr>" ).arg( mCellStyle, mErrStyle );
139 mReport += QLatin1String( "</tr></table>" );
140 mReport += QLatin1String( "<br>" );
141
142 const int width = expectedProvider->xSize();
143 const int height = expectedProvider->ySize();
144 std::unique_ptr< QgsRasterBlock > expectedBlock( expectedProvider->block( band, expectedProvider->extent(), width, height ) );
145 std::unique_ptr< QgsRasterBlock > verifiedBlock( verifiedProvider->block( band, expectedProvider->extent(), width, height ) );
146
147 if ( !expectedBlock || !expectedBlock->isValid() ||
148 !verifiedBlock || !verifiedBlock->isValid() )
149 {
150 allOk = false;
151 mReport += QLatin1String( "cannot read raster block" );
152 continue;
153 }
154
155 // compare data values
156 QString htmlTable = QStringLiteral( "<table style='%1'>" ).arg( mTabStyle );
157 for ( int row = 0; row < height; row ++ )
158 {
159 htmlTable += QLatin1String( "<tr>" );
160 for ( int col = 0; col < width; col ++ )
161 {
162 bool cellOk = true;
163 const double verifiedVal = verifiedBlock->value( row, col );
164 const double expectedVal = expectedBlock->value( row, col );
165
166 QString valStr;
167 if ( compare( verifiedVal, expectedVal, 0 ) )
168 {
169 valStr = QString::number( verifiedVal );
170 }
171 else
172 {
173 cellOk = false;
174 allOk = false;
175 valStr = QStringLiteral( "%1<br>%2" ).arg( verifiedVal ).arg( expectedVal );
176 }
177 htmlTable += QStringLiteral( "<td style='%1 %2'>%3</td>" ).arg( mCellStyle, cellOk ? mOkStyle : mErrStyle, valStr );
178 }
179 htmlTable += QLatin1String( "</tr>" );
180 }
181 htmlTable += QLatin1String( "</table>" );
182
183 mReport += htmlTable;
184 }
185 delete verifiedProvider;
186 delete expectedProvider;
187 return allOk;
188}
189
190void QgsRasterChecker::error( const QString &message, QString &report )
191{
192 report += QStringLiteral( "<font style='%1'>Error: " ).arg( mErrMsgStyle );
193 report += message;
194 report += QLatin1String( "</font>" );
195}
196
197double QgsRasterChecker::tolerance( double val, int places )
198{
199 // float precision is about 7 decimal digits, double about 16
200 // default places = 6
201 return 1. * std::pow( 10, std::round( std::log10( std::fabs( val ) ) - places ) );
202}
203
204QString QgsRasterChecker::compareHead()
205{
206 QString html;
207 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 );
208 return html;
209}
210
211void QgsRasterChecker::compare( const QString &paramName, int verifiedVal, int expectedVal, QString &report, bool &ok )
212{
213 const bool isEqual = verifiedVal == expectedVal;
214 compareRow( paramName, QString::number( verifiedVal ), QString::number( expectedVal ), report, isEqual, QString::number( verifiedVal - expectedVal ) );
215 if ( !isEqual )
216 ok = false;
217}
218
219void QgsRasterChecker::compare( const QString &paramName, Qgis::DataType verifiedVal, Qgis::DataType expectedVal, QString &report, bool &ok )
220{
221 const bool isEqual = verifiedVal == expectedVal;
222 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 ) ) );
223 if ( !isEqual )
224 ok = false;
225}
226
227bool QgsRasterChecker::compare( double verifiedVal, double expectedVal, double tolerance )
228{
229 // values may be nan
230 return ( std::isnan( verifiedVal ) && std::isnan( expectedVal ) ) || ( std::fabs( verifiedVal - expectedVal ) <= tolerance );
231}
232
233void QgsRasterChecker::compare( const QString &paramName, double verifiedVal, double expectedVal, QString &report, bool &ok, double tolerance )
234{
235 const bool isNearEqual = compare( verifiedVal, expectedVal, tolerance );
236 compareRow( paramName, QString::number( verifiedVal ), QString::number( expectedVal ), report, isNearEqual, QString::number( verifiedVal - expectedVal ), QString::number( tolerance ) );
237 if ( !isNearEqual )
238 ok = false;
239}
240
241void QgsRasterChecker::compareRow( const QString &paramName, const QString &verifiedVal, const QString &expectedVal, QString &report, bool ok, const QString &difference, const QString &tolerance )
242{
243 report += QLatin1String( "<tr>\n" );
244 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 );
245 report += QStringLiteral( "<td style='%1'>%2</td>\n" ).arg( mCellStyle, difference );
246 report += QStringLiteral( "<td style='%1'>%2</td>\n" ).arg( mCellStyle, tolerance );
247 report += QLatin1String( "</tr>" );
248}
DataType
Raster data types.
Definition qgis.h:372
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
Q_INVOKABLE 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.