QGIS API Documentation  master-6227475
src/core/raster/qgsrasterchecker.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002      qgsrasterchecker.cpp
00003      --------------------------------------
00004     Date                 : 5 Sep 2012
00005     Copyright            : (C) 2012 by Radim Blazek
00006     Email                : radim dot blazek at gmail dot com
00007  ***************************************************************************
00008  *                                                                         *
00009  *   This program is free software; you can redistribute it and/or modify  *
00010  *   it under the terms of the GNU General Public License as published by  *
00011  *   the Free Software Foundation; either version 2 of the License, or     *
00012  *   (at your option) any later version.                                   *
00013  *                                                                         *
00014  ***************************************************************************/
00015 
00016 #include "qgsproviderregistry.h"
00017 #include "qgsrasterchecker.h"
00018 #include "qgsrasterdataprovider.h"
00019 #include "qgsrasterlayer.h"
00020 
00021 #include <qmath.h>
00022 #include <QColor>
00023 #include <QPainter>
00024 #include <QImage>
00025 #include <QTime>
00026 #include <QCryptographicHash>
00027 #include <QByteArray>
00028 #include <QDebug>
00029 #include <QBuffer>
00030 
00031 QgsRasterChecker::QgsRasterChecker( ) :
00032     mReport( "" )
00033 {
00034   mTabStyle = "border-spacing: 0px; border-width: 1px 1px 0 0; border-style: solid;";
00035   mCellStyle = "border-width: 0 0 1px 1px; border-style: solid; font-size: smaller; text-align: center;";
00036   mOkStyle = "background: #00ff00;";
00037   mErrStyle = "background: #ff0000;";
00038   mErrMsgStyle = "color: #ff0000;";
00039 }
00040 
00041 bool QgsRasterChecker::runTest( QString theVerifiedKey, QString theVerifiedUri,
00042                                 QString theExpectedKey, QString theExpectedUri )
00043 {
00044   bool ok = true;
00045   mReport += "\n\n";
00046 
00047   //QgsRasterDataProvider* verifiedProvider = QgsRasterLayer::loadProvider( theVerifiedKey, theVerifiedUri );
00048   QgsRasterDataProvider* verifiedProvider = ( QgsRasterDataProvider* ) QgsProviderRegistry::instance()->provider( theVerifiedKey, theVerifiedUri );
00049   if ( !verifiedProvider || !verifiedProvider->isValid() )
00050   {
00051     error( QString( "Cannot load provider %1 with URI: %2" ).arg( theVerifiedKey ).arg( theVerifiedUri ), mReport );
00052     ok = false;
00053   }
00054 
00055   //QgsRasterDataProvider* expectedProvider = QgsRasterLayer::loadProvider( theExpectedKey, theExpectedUri );
00056   QgsRasterDataProvider* expectedProvider = ( QgsRasterDataProvider* ) QgsProviderRegistry::instance()->provider( theExpectedKey, theExpectedUri );
00057   if ( !expectedProvider || !expectedProvider->isValid() )
00058   {
00059     error( QString( "Cannot load provider %1 with URI: %2" ).arg( theExpectedKey ).arg( theExpectedUri ), mReport );
00060     ok = false;
00061   }
00062 
00063   if ( !ok ) return false;
00064 
00065   mReport += QString( "Verified URI: %1<br>" ).arg( theVerifiedUri.replace( "&", "&amp;" ) );
00066   mReport += QString( "Expected URI: %1<br>" ).arg( theExpectedUri.replace( "&", "&amp;" ) );
00067 
00068   mReport += "<br>";
00069   mReport += QString( "<table style='%1'>\n" ).arg( mTabStyle );
00070   mReport += compareHead();
00071 
00072   compare( "Band count", verifiedProvider->bandCount(), expectedProvider->bandCount(), mReport, ok );
00073 
00074   compare( "Width", verifiedProvider->xSize(), expectedProvider->xSize(), mReport, ok );
00075   compare( "Height", verifiedProvider->ySize(), expectedProvider->ySize(), mReport, ok );
00076 
00077   compareRow( "Extent", verifiedProvider->extent().toString(), expectedProvider->extent().toString(), mReport, verifiedProvider->extent() == expectedProvider->extent() );
00078 
00079   if ( verifiedProvider->extent() != expectedProvider->extent() ) ok = false;
00080 
00081 
00082   mReport += "</table>\n";
00083 
00084   if ( !ok ) return false;
00085 
00086   bool allOk = true;
00087   for ( int band = 1; band <= expectedProvider->bandCount(); band++ )
00088   {
00089     bool bandOk = true;
00090     mReport += QString( "<h3>Band %1</h3>\n" ).arg( band );
00091     mReport += QString( "<table style='%1'>\n" ).arg( mTabStyle );
00092     mReport += compareHead();
00093 
00094     // Data types may differ (?)
00095     bool typesOk = true;
00096     compare( "Source data type", verifiedProvider->srcDataType( band ), expectedProvider->srcDataType( band ), mReport, typesOk );
00097     compare( "Data type", verifiedProvider->dataType( band ), expectedProvider->dataType( band ), mReport, typesOk ) ;
00098 
00099     // TODO: not yet sure if noDataValue() should exist at all
00100     //compare( "No data (NULL) value", verifiedProvider->noDataValue( band ), expectedProvider->noDataValue( band ), mReport, typesOk );
00101 
00102     bool statsOk = true;
00103     QgsRasterBandStats verifiedStats =  verifiedProvider->bandStatistics( band );
00104     QgsRasterBandStats expectedStats =  expectedProvider->bandStatistics( band );
00105 
00106     // Min/max may 'slightly' differ, for big numbers however, the difference may
00107     // be quite big, for example for Float32 with max -3.332e+38, the difference is 1.47338e+24
00108     double tol = tolerance( expectedStats.minimumValue );
00109     compare( "Minimum value", verifiedStats.minimumValue, expectedStats.minimumValue, mReport, statsOk, tol );
00110     tol = tolerance( expectedStats.maximumValue );
00111     compare( "Maximum value", verifiedStats.maximumValue, expectedStats.maximumValue, mReport, statsOk, tol );
00112 
00113     // TODO: enable once fixed (WCS excludes nulls but GDAL does not)
00114     //compare( "Cells count", verifiedStats.elementCount, expectedStats.elementCount, mReport, statsOk );
00115 
00116     tol = tolerance( expectedStats.mean );
00117     compare( "Mean", verifiedStats.mean, expectedStats.mean, mReport, statsOk, tol );
00118 
00119     // stdDev usually differ significantly
00120     tol = tolerance( expectedStats.stdDev, 1 );
00121     compare( "Standard deviation", verifiedStats.stdDev, expectedStats.stdDev, mReport, statsOk, tol );
00122 
00123     mReport += "</table>";
00124     mReport += "<br>";
00125 
00126     if ( !bandOk )
00127     {
00128       allOk = false;
00129       continue;
00130     }
00131 
00132     if ( !statsOk || !typesOk )
00133     {
00134       allOk = false;
00135       // create values table anyway so that values are available
00136     }
00137 
00138     mReport += "<table><tr>";
00139     mReport += "<td>Data comparison</td>";
00140     mReport += QString( "<td style='%1 %2 border: 1px solid'>correct&nbsp;value</td>" ).arg( mCellStyle ).arg( mOkStyle );
00141     mReport += "<td></td>";
00142     mReport += QString( "<td style='%1 %2 border: 1px solid'>wrong&nbsp;value<br>expected value</td></tr>" ).arg( mCellStyle ).arg( mErrStyle );
00143     mReport += "</tr></table>";
00144     mReport += "<br>";
00145 
00146     int width = expectedProvider->xSize();
00147     int height = expectedProvider->ySize();
00148     QgsRasterBlock *expectedBlock = expectedProvider->block( band, expectedProvider->extent(), width, height );
00149     QgsRasterBlock *verifiedBlock = verifiedProvider->block( band, expectedProvider->extent(), width, height );
00150 
00151     if ( !expectedBlock || !expectedBlock->isValid() ||
00152          !verifiedBlock || !verifiedBlock->isValid() )
00153     {
00154       allOk = false;
00155       mReport += "cannot read raster block";
00156       continue;
00157     }
00158 
00159     // compare data values
00160     QString htmlTable = QString( "<table style='%1'>" ).arg( mTabStyle );
00161     for ( int row = 0; row < height; row ++ )
00162     {
00163       htmlTable += "<tr>";
00164       for ( int col = 0; col < width; col ++ )
00165       {
00166         bool cellOk = true;
00167         double verifiedVal = verifiedBlock->value( row, col );
00168         double expectedVal = expectedBlock->value( row, col );
00169 
00170         QString valStr;
00171         if ( compare( verifiedVal, expectedVal, 0 ) )
00172         {
00173           valStr = QString( "%1" ).arg( verifiedVal );
00174         }
00175         else
00176         {
00177           cellOk = false;
00178           allOk = false;
00179           valStr = QString( "%1<br>%2" ).arg( verifiedVal ).arg( expectedVal );
00180         }
00181         htmlTable += QString( "<td style='%1 %2'>%3</td>" ).arg( mCellStyle ).arg( cellOk ? mOkStyle : mErrStyle ).arg( valStr );
00182       }
00183       htmlTable += "</tr>";
00184     }
00185     htmlTable += "</table>";
00186 
00187     mReport += htmlTable;
00188 
00189     delete expectedBlock;
00190     delete verifiedBlock;
00191   }
00192   delete verifiedProvider;
00193   delete expectedProvider;
00194   return allOk;
00195 }
00196 
00197 void QgsRasterChecker::error( QString theMessage, QString &theReport )
00198 {
00199   theReport += QString( "<font style='%1'>Error: " ).arg( mErrMsgStyle );
00200   theReport += theMessage;
00201   theReport += "</font>";
00202 }
00203 
00204 double QgsRasterChecker::tolerance( double val, int places )
00205 {
00206   // float precision is about 7 decimal digits, double about 16
00207   // default places = 6
00208   return 1. * qPow( 10, qRound( log10( qAbs( val ) ) - places ) );
00209 }
00210 
00211 QString QgsRasterChecker::compareHead()
00212 {
00213   QString html;
00214   html += QString( "<tr><th style='%1'>Param name</th><th style='%1'>Verified value</th><th style='%1'>Eexpected value</th><th style='%1'>Difference</th><th style='%1'>Tolerance</th></tr>" ).arg( mCellStyle );
00215   return html;
00216 }
00217 
00218 void QgsRasterChecker::compare( QString theParamName, int verifiedVal, int expectedVal, QString &theReport, bool &theOk )
00219 {
00220   bool ok = verifiedVal == expectedVal;
00221   compareRow( theParamName, QString::number( verifiedVal ), QString::number( expectedVal ), theReport, ok, QString::number( verifiedVal - expectedVal ) );
00222   if ( !ok ) theOk = false;
00223 }
00224 
00225 bool QgsRasterChecker::compare( double verifiedVal, double expectedVal, double theTolerance )
00226 {
00227   // values may be nan
00228   return ( qIsNaN( verifiedVal ) && qIsNaN( expectedVal ) ) || ( qAbs( verifiedVal - expectedVal ) <= theTolerance );
00229 }
00230 
00231 void QgsRasterChecker::compare( QString theParamName, double verifiedVal, double expectedVal, QString &theReport, bool &theOk, double theTolerance )
00232 {
00233   bool ok = compare( verifiedVal, expectedVal, theTolerance );
00234   compareRow( theParamName, QString::number( verifiedVal ), QString::number( expectedVal ), theReport, ok, QString::number( verifiedVal - expectedVal ), QString::number( theTolerance ) );
00235   if ( !ok ) theOk = false;
00236 }
00237 
00238 void QgsRasterChecker::compareRow( QString theParamName, QString verifiedVal, QString expectedVal, QString &theReport, bool theOk, QString theDifference, QString theTolerance )
00239 {
00240   theReport += "<tr>\n";
00241   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 );
00242   theReport += QString( "<td style='%1'>%2</td>\n" ).arg( mCellStyle ).arg( theDifference );
00243   theReport += QString( "<td style='%1'>%2</td>\n" ).arg( mCellStyle ).arg( theTolerance );
00244   theReport += "</tr>";
00245 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines