QGIS API Documentation  2.0.1-Dufour
 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 }