QGIS API Documentation  2.11.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
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( QString theVerifiedKey, QString theVerifiedUri,
42  QString theExpectedKey, QString theExpectedUri )
43 {
44  bool ok = true;
45  mReport += "\n\n";
46 
47  //QgsRasterDataProvider* verifiedProvider = QgsRasterLayer::loadProvider( theVerifiedKey, theVerifiedUri );
48  QgsRasterDataProvider* verifiedProvider = ( QgsRasterDataProvider* ) QgsProviderRegistry::instance()->provider( theVerifiedKey, theVerifiedUri );
49  if ( !verifiedProvider || !verifiedProvider->isValid() )
50  {
51  error( QString( "Cannot load provider %1 with URI: %2" ).arg( theVerifiedKey ).arg( theVerifiedUri ), mReport );
52  ok = false;
53  }
54 
55  //QgsRasterDataProvider* expectedProvider = QgsRasterLayer::loadProvider( theExpectedKey, theExpectedUri );
56  QgsRasterDataProvider* expectedProvider = ( QgsRasterDataProvider* ) QgsProviderRegistry::instance()->provider( theExpectedKey, theExpectedUri );
57  if ( !expectedProvider || !expectedProvider->isValid() )
58  {
59  error( QString( "Cannot load provider %1 with URI: %2" ).arg( theExpectedKey ).arg( 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  // TODO: not yet sure if noDataValue() should exist at all
99  //compare( "No data (NULL) value", verifiedProvider->noDataValue( band ), expectedProvider->noDataValue( band ), mReport, typesOk );
100 
101  bool statsOk = true;
102  QgsRasterBandStats verifiedStats = verifiedProvider->bandStatistics( band );
103  QgsRasterBandStats expectedStats = expectedProvider->bandStatistics( band );
104 
105  // Min/max may 'slightly' differ, for big numbers however, the difference may
106  // be quite big, for example for Float32 with max -3.332e+38, the difference is 1.47338e+24
107  double tol = tolerance( expectedStats.minimumValue );
108  compare( "Minimum value", verifiedStats.minimumValue, expectedStats.minimumValue, mReport, statsOk, tol );
109  tol = tolerance( expectedStats.maximumValue );
110  compare( "Maximum value", verifiedStats.maximumValue, expectedStats.maximumValue, mReport, statsOk, tol );
111 
112  // TODO: enable once fixed (WCS excludes nulls but GDAL does not)
113  //compare( "Cells count", verifiedStats.elementCount, expectedStats.elementCount, mReport, statsOk );
114 
115  tol = tolerance( expectedStats.mean );
116  compare( "Mean", verifiedStats.mean, expectedStats.mean, mReport, statsOk, tol );
117 
118  // stdDev usually differ significantly
119  tol = tolerance( expectedStats.stdDev, 1 );
120  compare( "Standard deviation", verifiedStats.stdDev, expectedStats.stdDev, mReport, statsOk, tol );
121 
122  mReport += "</table>";
123  mReport += "<br>";
124 
125  if ( !statsOk || !typesOk )
126  {
127  allOk = false;
128  // create values table anyway so that values are available
129  }
130 
131  mReport += "<table><tr>";
132  mReport += "<td>Data comparison</td>";
133  mReport += QString( "<td style='%1 %2 border: 1px solid'>correct&nbsp;value</td>" ).arg( mCellStyle ).arg( mOkStyle );
134  mReport += "<td></td>";
135  mReport += QString( "<td style='%1 %2 border: 1px solid'>wrong&nbsp;value<br>expected value</td></tr>" ).arg( mCellStyle ).arg( mErrStyle );
136  mReport += "</tr></table>";
137  mReport += "<br>";
138 
139  int width = expectedProvider->xSize();
140  int height = expectedProvider->ySize();
141  QgsRasterBlock *expectedBlock = expectedProvider->block( band, expectedProvider->extent(), width, height );
142  QgsRasterBlock *verifiedBlock = verifiedProvider->block( band, expectedProvider->extent(), width, height );
143 
144  if ( !expectedBlock || !expectedBlock->isValid() ||
145  !verifiedBlock || !verifiedBlock->isValid() )
146  {
147  allOk = false;
148  mReport += "cannot read raster block";
149  continue;
150  }
151 
152  // compare data values
153  QString htmlTable = QString( "<table style='%1'>" ).arg( mTabStyle );
154  for ( int row = 0; row < height; row ++ )
155  {
156  htmlTable += "<tr>";
157  for ( int col = 0; col < width; col ++ )
158  {
159  bool cellOk = true;
160  double verifiedVal = verifiedBlock->value( row, col );
161  double expectedVal = expectedBlock->value( row, col );
162 
163  QString valStr;
164  if ( compare( verifiedVal, expectedVal, 0 ) )
165  {
166  valStr = QString( "%1" ).arg( verifiedVal );
167  }
168  else
169  {
170  cellOk = false;
171  allOk = false;
172  valStr = QString( "%1<br>%2" ).arg( verifiedVal ).arg( expectedVal );
173  }
174  htmlTable += QString( "<td style='%1 %2'>%3</td>" ).arg( mCellStyle ).arg( cellOk ? mOkStyle : mErrStyle ).arg( valStr );
175  }
176  htmlTable += "</tr>";
177  }
178  htmlTable += "</table>";
179 
180  mReport += htmlTable;
181 
182  delete expectedBlock;
183  delete verifiedBlock;
184  }
185  delete verifiedProvider;
186  delete expectedProvider;
187  return allOk;
188 }
189 
190 void QgsRasterChecker::error( QString theMessage, QString &theReport )
191 {
192  theReport += QString( "<font style='%1'>Error: " ).arg( mErrMsgStyle );
193  theReport += theMessage;
194  theReport += "</font>";
195 }
196 
197 double 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. * qPow( 10, qRound( log10( qAbs( val ) ) - places ) );
202 }
203 
204 QString QgsRasterChecker::compareHead()
205 {
206  QString html;
207  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 );
208  return html;
209 }
210 
211 void QgsRasterChecker::compare( QString theParamName, int verifiedVal, int expectedVal, QString &theReport, bool &theOk )
212 {
213  bool ok = verifiedVal == expectedVal;
214  compareRow( theParamName, QString::number( verifiedVal ), QString::number( expectedVal ), theReport, ok, QString::number( verifiedVal - expectedVal ) );
215  if ( !ok ) theOk = false;
216 }
217 
218 bool QgsRasterChecker::compare( double verifiedVal, double expectedVal, double theTolerance )
219 {
220  // values may be nan
221  return ( qIsNaN( verifiedVal ) && qIsNaN( expectedVal ) ) || ( qAbs( verifiedVal - expectedVal ) <= theTolerance );
222 }
223 
224 void QgsRasterChecker::compare( QString theParamName, double verifiedVal, double expectedVal, QString &theReport, bool &theOk, double theTolerance )
225 {
226  bool ok = compare( verifiedVal, expectedVal, theTolerance );
227  compareRow( theParamName, QString::number( verifiedVal ), QString::number( expectedVal ), theReport, ok, QString::number( verifiedVal - expectedVal ), QString::number( theTolerance ) );
228  if ( !ok ) theOk = false;
229 }
230 
231 void QgsRasterChecker::compareRow( QString theParamName, QString verifiedVal, QString expectedVal, QString &theReport, bool theOk, QString theDifference, QString theTolerance )
232 {
233  theReport += "<tr>\n";
234  theReport += QString( "<td style='%1'>%2</td><td style='%1 %3'>%4</td><td style='%1'>%5</td>\n" ).arg( mCellStyle ).arg( theParamName ).arg( theOk ? mOkStyle : mErrStyle ).arg( verifiedVal ).arg( expectedVal );
235  theReport += QString( "<td style='%1'>%2</td>\n" ).arg( mCellStyle ).arg( theDifference );
236  theReport += QString( "<td style='%1'>%2</td>\n" ).arg( mCellStyle ).arg( theTolerance );
237  theReport += "</tr>";
238 }
virtual int bandCount() const =0
Get number of bands.
bool isValid() const
Returns true if the block is valid (correctly filled with data).
static QgsProviderRegistry * instance(QString pluginPath=QString::null)
means of accessing canonical single instance
double maximumValue
The maximum cell value in the raster band.
virtual int ySize() const
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...
double value(int row, int column) const
Read a single value if type of block is numeric.
virtual QgsRasterBlock * block(int theBandNo, const QgsRectangle &theExtent, int theWidth, int theHeight) override
Read block of data using given extent and size.
bool runTest(QString theVerifiedKey, QString theVerifiedUri, QString theExpectedKey, QString theExpectedUri)
Test using renderer to generate the image to be compared.
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)
virtual int xSize() const
Get raster size.
virtual bool isValid()=0
Returns true if this is a valid layer.
double minimumValue
The minimum cell value in the raster band.
QString toString(bool automaticPrecision=false) const
returns string representation of form xmin,ymin xmax,ymax
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
Base class for raster data providers.