QGIS API Documentation  2.8.2-Wien
 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 }