QGIS API Documentation  2.18.21-Las Palmas (9fba24a)
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"
18 #include "qgsrasterdataprovider.h"
19 #include "qgsrasterlayer.h"
20 
21 #include <qmath.h>
22 #include <QColor>
23 #include <QPainter>
24 #include <QImage>
25 #include <QTime>
26 #include <QCryptographicHash>
27 #include <QByteArray>
28 #include <QDebug>
29 #include <QBuffer>
30 
32  : mReport( "" )
33 {
34  mTabStyle = "border-spacing: 0px; border-width: 1px 1px 0 0; border-style: solid;";
35  mCellStyle = "border-width: 0 0 1px 1px; border-style: solid; font-size: smaller; text-align: center;";
36  mOkStyle = "background: #00ff00;";
37  mErrStyle = "background: #ff0000;";
38  mErrMsgStyle = "color: #ff0000;";
39 }
40 
41 bool QgsRasterChecker::runTest( const QString& theVerifiedKey, QString theVerifiedUri,
42  const QString& theExpectedKey, QString theExpectedUri )
43 {
44  bool ok = true;
45  mReport += "\n\n";
46 
47  //QgsRasterDataProvider* verifiedProvider = QgsRasterLayer::loadProvider( theVerifiedKey, theVerifiedUri );
48  QgsRasterDataProvider* verifiedProvider = dynamic_cast< QgsRasterDataProvider* >( QgsProviderRegistry::instance()->provider( theVerifiedKey, theVerifiedUri ) );
49  if ( !verifiedProvider || !verifiedProvider->isValid() )
50  {
51  error( QString( "Cannot load provider %1 with URI: %2" ).arg( theVerifiedKey, theVerifiedUri ), mReport );
52  ok = false;
53  }
54 
55  //QgsRasterDataProvider* expectedProvider = QgsRasterLayer::loadProvider( theExpectedKey, theExpectedUri );
56  QgsRasterDataProvider* expectedProvider = dynamic_cast< QgsRasterDataProvider* >( QgsProviderRegistry::instance()->provider( theExpectedKey, theExpectedUri ) );
57  if ( !expectedProvider || !expectedProvider->isValid() )
58  {
59  error( QString( "Cannot load provider %1 with URI: %2" ).arg( theExpectedKey, theExpectedUri ), mReport );
60  ok = false;
61  }
62 
63  if ( !ok ) return false;
64 
65  mReport += QString( "Verified URI: %1<br>" ).arg( theVerifiedUri.replace( '&', "&amp;" ) );
66  mReport += QString( "Expected URI: %1<br>" ).arg( theExpectedUri.replace( '&', "&amp;" ) );
67 
68  mReport += "<br>";
69  mReport += QString( "<table style='%1'>\n" ).arg( mTabStyle );
70  mReport += compareHead();
71 
72  compare( "Band count", verifiedProvider->bandCount(), expectedProvider->bandCount(), mReport, ok );
73 
74  compare( "Width", verifiedProvider->xSize(), expectedProvider->xSize(), mReport, ok );
75  compare( "Height", verifiedProvider->ySize(), expectedProvider->ySize(), mReport, ok );
76 
77  compareRow( "Extent", verifiedProvider->extent().toString(), expectedProvider->extent().toString(), mReport, verifiedProvider->extent() == expectedProvider->extent() );
78 
79  if ( verifiedProvider->extent() != expectedProvider->extent() ) ok = false;
80 
81 
82  mReport += "</table>\n";
83 
84  if ( !ok ) return false;
85 
86  bool allOk = true;
87  for ( int band = 1; band <= expectedProvider->bandCount(); band++ )
88  {
89  mReport += QString( "<h3>Band %1</h3>\n" ).arg( band );
90  mReport += QString( "<table style='%1'>\n" ).arg( mTabStyle );
91  mReport += compareHead();
92 
93  // Data types may differ (?)
94  bool typesOk = true;
95  compare( "Source data type", verifiedProvider->srcDataType( band ), expectedProvider->srcDataType( band ), mReport, typesOk );
96  compare( "Data type", verifiedProvider->dataType( band ), expectedProvider->dataType( band ), mReport, typesOk );
97 
98  // Check nodata
99  bool noDataOk = true;
100  compare( "No data (NULL) value existence flag", verifiedProvider->srcHasNoDataValue( band ), expectedProvider->srcHasNoDataValue( band ), mReport, noDataOk );
101  if ( verifiedProvider->srcHasNoDataValue( band ) && expectedProvider->srcHasNoDataValue( band ) )
102  {
103  compare( "No data (NULL) value", verifiedProvider->srcNoDataValue( band ), expectedProvider->srcNoDataValue( band ), mReport, noDataOk );
104  }
105 
106  bool statsOk = true;
107  QgsRasterBandStats verifiedStats = verifiedProvider->bandStatistics( band );
108  QgsRasterBandStats expectedStats = expectedProvider->bandStatistics( band );
109 
110  // Min/max may 'slightly' differ, for big numbers however, the difference may
111  // be quite big, for example for Float32 with max -3.332e+38, the difference is 1.47338e+24
112  double tol = tolerance( expectedStats.minimumValue );
113  compare( "Minimum value", verifiedStats.minimumValue, expectedStats.minimumValue, mReport, statsOk, tol );
114  tol = tolerance( expectedStats.maximumValue );
115  compare( "Maximum value", verifiedStats.maximumValue, expectedStats.maximumValue, mReport, statsOk, tol );
116 
117  // TODO: enable once fixed (WCS excludes nulls but GDAL does not)
118  //compare( "Cells count", verifiedStats.elementCount, expectedStats.elementCount, mReport, statsOk );
119 
120  tol = tolerance( expectedStats.mean );
121  compare( "Mean", verifiedStats.mean, expectedStats.mean, mReport, statsOk, tol );
122 
123  // stdDev usually differ significantly
124  tol = tolerance( expectedStats.stdDev, 1 );
125  compare( "Standard deviation", verifiedStats.stdDev, expectedStats.stdDev, mReport, statsOk, tol );
126 
127  mReport += "</table>";
128  mReport += "<br>";
129 
130  if ( !statsOk || !typesOk || !noDataOk )
131  {
132  allOk = false;
133  // create values table anyway so that values are available
134  }
135 
136  mReport += "<table><tr>";
137  mReport += "<td>Data comparison</td>";
138  mReport += QString( "<td style='%1 %2 border: 1px solid'>correct&nbsp;value</td>" ).arg( mCellStyle, mOkStyle );
139  mReport += "<td></td>";
140  mReport += QString( "<td style='%1 %2 border: 1px solid'>wrong&nbsp;value<br>expected value</td></tr>" ).arg( mCellStyle, mErrStyle );
141  mReport += "</tr></table>";
142  mReport += "<br>";
143 
144  int width = expectedProvider->xSize();
145  int height = expectedProvider->ySize();
146  QgsRasterBlock *expectedBlock = expectedProvider->block( band, expectedProvider->extent(), width, height );
147  QgsRasterBlock *verifiedBlock = verifiedProvider->block( band, expectedProvider->extent(), width, height );
148 
149  if ( !expectedBlock || !expectedBlock->isValid() ||
150  !verifiedBlock || !verifiedBlock->isValid() )
151  {
152  allOk = false;
153  mReport += "cannot read raster block";
154  continue;
155  }
156 
157  // compare data values
158  QString htmlTable = QString( "<table style='%1'>" ).arg( mTabStyle );
159  for ( int row = 0; row < height; row ++ )
160  {
161  htmlTable += "<tr>";
162  for ( int col = 0; col < width; col ++ )
163  {
164  bool cellOk = true;
165  double verifiedVal = verifiedBlock->value( row, col );
166  double expectedVal = expectedBlock->value( row, col );
167 
168  QString valStr;
169  if ( compare( verifiedVal, expectedVal, 0 ) )
170  {
171  valStr = QString( "%1" ).arg( verifiedVal );
172  }
173  else
174  {
175  cellOk = false;
176  allOk = false;
177  valStr = QString( "%1<br>%2" ).arg( verifiedVal ).arg( expectedVal );
178  }
179  htmlTable += QString( "<td style='%1 %2'>%3</td>" ).arg( mCellStyle, cellOk ? mOkStyle : mErrStyle, valStr );
180  }
181  htmlTable += "</tr>";
182  }
183  htmlTable += "</table>";
184 
185  mReport += htmlTable;
186 
187  delete expectedBlock;
188  delete verifiedBlock;
189  }
190  delete verifiedProvider;
191  delete expectedProvider;
192  return allOk;
193 }
194 
195 void QgsRasterChecker::error( const QString& theMessage, QString &theReport )
196 {
197  theReport += QString( "<font style='%1'>Error: " ).arg( mErrMsgStyle );
198  theReport += theMessage;
199  theReport += "</font>";
200 }
201 
202 double QgsRasterChecker::tolerance( double val, int places )
203 {
204  // float precision is about 7 decimal digits, double about 16
205  // default places = 6
206  return 1. * qPow( 10, qRound( log10( qAbs( val ) ) - places ) );
207 }
208 
209 QString QgsRasterChecker::compareHead()
210 {
211  QString html;
212  html += QString( "<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 );
213  return html;
214 }
215 
216 void QgsRasterChecker::compare( const QString& theParamName, int verifiedVal, int expectedVal, QString &theReport, bool &theOk )
217 {
218  bool ok = verifiedVal == expectedVal;
219  compareRow( theParamName, QString::number( verifiedVal ), QString::number( expectedVal ), theReport, ok, QString::number( verifiedVal - expectedVal ) );
220  if ( !ok ) theOk = false;
221 }
222 
223 bool QgsRasterChecker::compare( double verifiedVal, double expectedVal, double theTolerance )
224 {
225  // values may be nan
226  return ( qIsNaN( verifiedVal ) && qIsNaN( expectedVal ) ) || ( qAbs( verifiedVal - expectedVal ) <= theTolerance );
227 }
228 
229 void QgsRasterChecker::compare( const QString& theParamName, double verifiedVal, double expectedVal, QString &theReport, bool &theOk, double theTolerance )
230 {
231  bool ok = compare( verifiedVal, expectedVal, theTolerance );
232  compareRow( theParamName, QString::number( verifiedVal ), QString::number( expectedVal ), theReport, ok, QString::number( verifiedVal - expectedVal ), QString::number( theTolerance ) );
233  if ( !ok ) theOk = false;
234 }
235 
236 void QgsRasterChecker::compareRow( const QString& theParamName, const QString& verifiedVal, const QString& expectedVal, QString &theReport, bool theOk, const QString& theDifference, const QString& theTolerance )
237 {
238  theReport += "<tr>\n";
239  theReport += QString( "<td style='%1'>%2</td><td style='%1 %3'>%4</td><td style='%1'>%5</td>\n" ).arg( mCellStyle, theParamName, theOk ? mOkStyle : mErrStyle, verifiedVal, expectedVal );
240  theReport += QString( "<td style='%1'>%2</td>\n" ).arg( mCellStyle, theDifference );
241  theReport += QString( "<td style='%1'>%2</td>\n" ).arg( mCellStyle, theTolerance );
242  theReport += "</tr>";
243 }
virtual int bandCount() const =0
Get number of bands.
static QgsProviderRegistry * instance(const QString &pluginPath=QString::null)
Means of accessing canonical single instance.
bool isValid() const
Returns true if the block is valid (correctly filled with data).
double maximumValue
The maximum cell value in the raster band.
virtual int ySize() const
QString toString(bool automaticPrecision=false) const
returns string representation of form xmin,ymin xmax,ymax
virtual QgsRasterBandStats bandStatistics(int theBandNo, int theStats=QgsRasterBandStats::All, const QgsRectangle &theExtent=QgsRectangle(), int theSampleSize=0)
Get band statistics.
double stdDev
The standard deviation of the cell values.
QString number(int n, int base)
The RasterBandStats struct is a container for statistics about a single raster band.
double mean
The mean cell value for the band.
Raster data container.
QgsDataProvider * provider(const QString &providerKey, const QString &dataSource)
Create an instance of the provider.
virtual QGis::DataType srcDataType(int bandNo) const override=0
Returns source data type for the band specified by number, source data type may be shorter than dataT...
virtual QgsRasterBlock * block(int theBandNo, const QgsRectangle &theExtent, int theWidth, int theHeight) override
Read block of data using given extent and size.
virtual QgsRectangle extent() override=0
Get the extent of the data source.
virtual QGis::DataType dataType(int bandNo) const override=0
Returns data type for the band specified by number.
QString & replace(int position, int n, QChar after)
bool runTest(const QString &theVerifiedKey, QString theVerifiedUri, const QString &theExpectedKey, QString theExpectedUri)
Test using renderer to generate the image to be compared.
virtual bool isValid()=0
Returns true if this is a valid layer.
double value(int row, int column) const
Read a single value if type of block is numeric.
double minimumValue
The minimum cell value in the raster band.
virtual bool srcHasNoDataValue(int bandNo) const
Return true if source band has no data value.
virtual double srcNoDataValue(int bandNo) const
Value representing no data value.
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
virtual int xSize() const
Get raster size.
Base class for raster data providers.