QGIS API Documentation  2.4.0-Chugiak
 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  bool bandOk = true;
90  mReport += QString( "<h3>Band %1</h3>\n" ).arg( band );
91  mReport += QString( "<table style='%1'>\n" ).arg( mTabStyle );
92  mReport += compareHead();
93 
94  // Data types may differ (?)
95  bool typesOk = true;
96  compare( "Source data type", verifiedProvider->srcDataType( band ), expectedProvider->srcDataType( band ), mReport, typesOk );
97  compare( "Data type", verifiedProvider->dataType( band ), expectedProvider->dataType( band ), mReport, typesOk ) ;
98 
99  // TODO: not yet sure if noDataValue() should exist at all
100  //compare( "No data (NULL) value", verifiedProvider->noDataValue( band ), expectedProvider->noDataValue( band ), mReport, typesOk );
101 
102  bool statsOk = true;
103  QgsRasterBandStats verifiedStats = verifiedProvider->bandStatistics( band );
104  QgsRasterBandStats expectedStats = expectedProvider->bandStatistics( band );
105 
106  // Min/max may 'slightly' differ, for big numbers however, the difference may
107  // be quite big, for example for Float32 with max -3.332e+38, the difference is 1.47338e+24
108  double tol = tolerance( expectedStats.minimumValue );
109  compare( "Minimum value", verifiedStats.minimumValue, expectedStats.minimumValue, mReport, statsOk, tol );
110  tol = tolerance( expectedStats.maximumValue );
111  compare( "Maximum value", verifiedStats.maximumValue, expectedStats.maximumValue, mReport, statsOk, tol );
112 
113  // TODO: enable once fixed (WCS excludes nulls but GDAL does not)
114  //compare( "Cells count", verifiedStats.elementCount, expectedStats.elementCount, mReport, statsOk );
115 
116  tol = tolerance( expectedStats.mean );
117  compare( "Mean", verifiedStats.mean, expectedStats.mean, mReport, statsOk, tol );
118 
119  // stdDev usually differ significantly
120  tol = tolerance( expectedStats.stdDev, 1 );
121  compare( "Standard deviation", verifiedStats.stdDev, expectedStats.stdDev, mReport, statsOk, tol );
122 
123  mReport += "</table>";
124  mReport += "<br>";
125 
126  if ( !bandOk )
127  {
128  allOk = false;
129  continue;
130  }
131 
132  if ( !statsOk || !typesOk )
133  {
134  allOk = false;
135  // create values table anyway so that values are available
136  }
137 
138  mReport += "<table><tr>";
139  mReport += "<td>Data comparison</td>";
140  mReport += QString( "<td style='%1 %2 border: 1px solid'>correct&nbsp;value</td>" ).arg( mCellStyle ).arg( mOkStyle );
141  mReport += "<td></td>";
142  mReport += QString( "<td style='%1 %2 border: 1px solid'>wrong&nbsp;value<br>expected value</td></tr>" ).arg( mCellStyle ).arg( mErrStyle );
143  mReport += "</tr></table>";
144  mReport += "<br>";
145 
146  int width = expectedProvider->xSize();
147  int height = expectedProvider->ySize();
148  QgsRasterBlock *expectedBlock = expectedProvider->block( band, expectedProvider->extent(), width, height );
149  QgsRasterBlock *verifiedBlock = verifiedProvider->block( band, expectedProvider->extent(), width, height );
150 
151  if ( !expectedBlock || !expectedBlock->isValid() ||
152  !verifiedBlock || !verifiedBlock->isValid() )
153  {
154  allOk = false;
155  mReport += "cannot read raster block";
156  continue;
157  }
158 
159  // compare data values
160  QString htmlTable = QString( "<table style='%1'>" ).arg( mTabStyle );
161  for ( int row = 0; row < height; row ++ )
162  {
163  htmlTable += "<tr>";
164  for ( int col = 0; col < width; col ++ )
165  {
166  bool cellOk = true;
167  double verifiedVal = verifiedBlock->value( row, col );
168  double expectedVal = expectedBlock->value( row, col );
169 
170  QString valStr;
171  if ( compare( verifiedVal, expectedVal, 0 ) )
172  {
173  valStr = QString( "%1" ).arg( verifiedVal );
174  }
175  else
176  {
177  cellOk = false;
178  allOk = false;
179  valStr = QString( "%1<br>%2" ).arg( verifiedVal ).arg( expectedVal );
180  }
181  htmlTable += QString( "<td style='%1 %2'>%3</td>" ).arg( mCellStyle ).arg( cellOk ? mOkStyle : mErrStyle ).arg( valStr );
182  }
183  htmlTable += "</tr>";
184  }
185  htmlTable += "</table>";
186 
187  mReport += htmlTable;
188 
189  delete expectedBlock;
190  delete verifiedBlock;
191  }
192  delete verifiedProvider;
193  delete expectedProvider;
194  return allOk;
195 }
196 
197 void QgsRasterChecker::error( QString theMessage, QString &theReport )
198 {
199  theReport += QString( "<font style='%1'>Error: " ).arg( mErrMsgStyle );
200  theReport += theMessage;
201  theReport += "</font>";
202 }
203 
204 double QgsRasterChecker::tolerance( double val, int places )
205 {
206  // float precision is about 7 decimal digits, double about 16
207  // default places = 6
208  return 1. * qPow( 10, qRound( log10( qAbs( val ) ) - places ) );
209 }
210 
212 {
213  QString html;
214  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 );
215  return html;
216 }
217 
218 void QgsRasterChecker::compare( QString theParamName, int verifiedVal, int expectedVal, QString &theReport, bool &theOk )
219 {
220  bool ok = verifiedVal == expectedVal;
221  compareRow( theParamName, QString::number( verifiedVal ), QString::number( expectedVal ), theReport, ok, QString::number( verifiedVal - expectedVal ) );
222  if ( !ok ) theOk = false;
223 }
224 
225 bool QgsRasterChecker::compare( double verifiedVal, double expectedVal, double theTolerance )
226 {
227  // values may be nan
228  return ( qIsNaN( verifiedVal ) && qIsNaN( expectedVal ) ) || ( qAbs( verifiedVal - expectedVal ) <= theTolerance );
229 }
230 
231 void QgsRasterChecker::compare( QString theParamName, double verifiedVal, double expectedVal, QString &theReport, bool &theOk, double theTolerance )
232 {
233  bool ok = compare( verifiedVal, expectedVal, theTolerance );
234  compareRow( theParamName, QString::number( verifiedVal ), QString::number( expectedVal ), theReport, ok, QString::number( verifiedVal - expectedVal ), QString::number( theTolerance ) );
235  if ( !ok ) theOk = false;
236 }
237 
238 void QgsRasterChecker::compareRow( QString theParamName, QString verifiedVal, QString expectedVal, QString &theReport, bool theOk, QString theDifference, QString theTolerance )
239 {
240  theReport += "<tr>\n";
241  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 );
242  theReport += QString( "<td style='%1'>%2</td>\n" ).arg( mCellStyle ).arg( theDifference );
243  theReport += QString( "<td style='%1'>%2</td>\n" ).arg( mCellStyle ).arg( theTolerance );
244  theReport += "</tr>";
245 }
virtual int bandCount() const =0
Get number of bands.
bool isValid() const
Returns true if the block is valid (correctly filled with data).
virtual QgsRasterBlock * block(int theBandNo, const QgsRectangle &theExtent, int theWidth, int theHeight)
Read block of data using given extent and size.
static QgsProviderRegistry * instance(QString pluginPath=QString::null)
means of accessing canonical single instance
double maximumValue
The maximum cell value in the raster band.
bool compare(double verifiedVal, double expectedVal, double theTolerance)
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.
The RasterBandStats struct is a container for statistics about a single raster band.
double mean
The mean cell value for the band.
void compareRow(QString theParamName, QString verifiedVal, QString expectedVal, QString &theReport, bool theOk, QString theDifference="", QString theTolerance="")
Raster data container.
QgsDataProvider * provider(const QString &providerKey, const QString &dataSource)
Create an instance of the provider.
double value(int row, int column) const
Read a single value if type of block is numeric.
virtual QGis::DataType srcDataType(int bandNo) const =0
Returns source data type for the band specified by number, source data type may be shorter than dataT...
virtual QgsRectangle extent()=0
Get the extent of the data source.
virtual QGis::DataType dataType(int bandNo) const =0
Returns data type for the band specified by number.
bool runTest(QString theVerifiedKey, QString theVerifiedUri, QString theExpectedKey, QString theExpectedUri)
Test using renderer to generate the image to be compared.
void error(QString theMessage, QString &theReport)
double tolerance(double val, int places=6)
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
Base class for raster data providers.