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