QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
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 <QColor>
22 #include <QPainter>
23 #include <QImage>
24 #include <QTime>
25 #include <QCryptographicHash>
26 #include <QByteArray>
27 #include <QDebug>
28 #include <QBuffer>
29 
31 {
32  mTabStyle = QStringLiteral( "border-spacing: 0px; border-width: 1px 1px 0 0; border-style: solid;" );
33  mCellStyle = QStringLiteral( "border-width: 0 0 1px 1px; border-style: solid; font-size: smaller; text-align: center;" );
34  mOkStyle = QStringLiteral( "background: #00ff00;" );
35  mErrStyle = QStringLiteral( "background: #ff0000;" );
36  mErrMsgStyle = QStringLiteral( "color: #ff0000;" );
37 }
38 
39 bool QgsRasterChecker::runTest( const QString &verifiedKey, QString verifiedUri,
40  const QString &expectedKey, QString expectedUri )
41 {
42  bool ok = true;
43  mReport += QLatin1String( "\n\n" );
44 
45  //QgsRasterDataProvider* verifiedProvider = QgsRasterLayer::loadProvider( verifiedKey, verifiedUri );
47  QgsRasterDataProvider *verifiedProvider = qobject_cast< QgsRasterDataProvider * >( QgsProviderRegistry::instance()->createProvider( verifiedKey, verifiedUri, options ) );
48  if ( !verifiedProvider || !verifiedProvider->isValid() )
49  {
50  error( QStringLiteral( "Cannot load provider %1 with URI: %2" ).arg( verifiedKey, verifiedUri ), mReport );
51  return false;
52  }
53 
54  //QgsRasterDataProvider* expectedProvider = QgsRasterLayer::loadProvider( expectedKey, expectedUri );
55  QgsRasterDataProvider *expectedProvider = qobject_cast< QgsRasterDataProvider * >( QgsProviderRegistry::instance()->createProvider( expectedKey, expectedUri, options ) );
56  if ( !expectedProvider || !expectedProvider->isValid() )
57  {
58  error( QStringLiteral( "Cannot load provider %1 with URI: %2" ).arg( expectedKey, expectedUri ), mReport );
59  return false;
60  }
61 
62  mReport += QStringLiteral( "Verified URI: %1<br>" ).arg( verifiedUri.replace( '&', QLatin1String( "&amp;" ) ) );
63  mReport += QStringLiteral( "Expected URI: %1<br>" ).arg( expectedUri.replace( '&', QLatin1String( "&amp;" ) ) );
64 
65  mReport += QLatin1String( "<br>" );
66  mReport += QStringLiteral( "<table style='%1'>\n" ).arg( mTabStyle );
67  mReport += compareHead();
68 
69  compare( QStringLiteral( "Band count" ), verifiedProvider->bandCount(), expectedProvider->bandCount(), mReport, ok );
70 
71  compare( QStringLiteral( "Width" ), verifiedProvider->xSize(), expectedProvider->xSize(), mReport, ok );
72  compare( QStringLiteral( "Height" ), verifiedProvider->ySize(), expectedProvider->ySize(), mReport, ok );
73 
74  compareRow( QStringLiteral( "Extent" ), verifiedProvider->extent().toString(), expectedProvider->extent().toString(), mReport, verifiedProvider->extent() == expectedProvider->extent() );
75 
76  if ( verifiedProvider->extent() != expectedProvider->extent() ) ok = false;
77 
78 
79  mReport += QLatin1String( "</table>\n" );
80 
81  if ( !ok ) return false;
82 
83  bool allOk = true;
84  for ( int band = 1; band <= expectedProvider->bandCount(); band++ )
85  {
86  mReport += QStringLiteral( "<h3>Band %1</h3>\n" ).arg( band );
87  mReport += QStringLiteral( "<table style='%1'>\n" ).arg( mTabStyle );
88  mReport += compareHead();
89 
90  // Data types may differ (?)
91  bool typesOk = true;
92  compare( QStringLiteral( "Source data type" ), verifiedProvider->sourceDataType( band ), expectedProvider->sourceDataType( band ), mReport, typesOk );
93  compare( QStringLiteral( "Data type" ), verifiedProvider->dataType( band ), expectedProvider->dataType( band ), mReport, typesOk );
94 
95  // Check nodata
96  bool noDataOk = true;
97  compare( QStringLiteral( "No data (NULL) value existence flag" ), verifiedProvider->sourceHasNoDataValue( band ), expectedProvider->sourceHasNoDataValue( band ), mReport, noDataOk );
98  if ( verifiedProvider->sourceHasNoDataValue( band ) && expectedProvider->sourceHasNoDataValue( band ) )
99  {
100  compare( QStringLiteral( "No data (NULL) value" ), verifiedProvider->sourceNoDataValue( band ), expectedProvider->sourceNoDataValue( band ), mReport, noDataOk );
101  }
102 
103  bool statsOk = true;
104  const QgsRasterBandStats verifiedStats = verifiedProvider->bandStatistics( band );
105  const QgsRasterBandStats expectedStats = expectedProvider->bandStatistics( band );
106 
107  // Min/max may 'slightly' differ, for big numbers however, the difference may
108  // be quite big, for example for Float32 with max -3.332e+38, the difference is 1.47338e+24
109  double tol = tolerance( expectedStats.minimumValue );
110  compare( QStringLiteral( "Minimum value" ), verifiedStats.minimumValue, expectedStats.minimumValue, mReport, statsOk, tol );
111  tol = tolerance( expectedStats.maximumValue );
112  compare( QStringLiteral( "Maximum value" ), verifiedStats.maximumValue, expectedStats.maximumValue, mReport, statsOk, tol );
113 
114  // TODO: enable once fixed (WCS excludes nulls but GDAL does not)
115  //compare( "Cells count", verifiedStats.elementCount, expectedStats.elementCount, mReport, statsOk );
116 
117  tol = tolerance( expectedStats.mean );
118  compare( QStringLiteral( "Mean" ), verifiedStats.mean, expectedStats.mean, mReport, statsOk, tol );
119 
120  // stdDev usually differ significantly
121  tol = tolerance( expectedStats.stdDev, 1 );
122  compare( QStringLiteral( "Standard deviation" ), verifiedStats.stdDev, expectedStats.stdDev, mReport, statsOk, tol );
123 
124  mReport += QLatin1String( "</table>" );
125  mReport += QLatin1String( "<br>" );
126 
127  if ( !statsOk || !typesOk || !noDataOk )
128  {
129  allOk = false;
130  // create values table anyway so that values are available
131  }
132 
133  mReport += QLatin1String( "<table><tr>" );
134  mReport += QLatin1String( "<td>Data comparison</td>" );
135  mReport += QStringLiteral( "<td style='%1 %2 border: 1px solid'>correct&nbsp;value</td>" ).arg( mCellStyle, mOkStyle );
136  mReport += QLatin1String( "<td></td>" );
137  mReport += QStringLiteral( "<td style='%1 %2 border: 1px solid'>wrong&nbsp;value<br>expected value</td></tr>" ).arg( mCellStyle, mErrStyle );
138  mReport += QLatin1String( "</tr></table>" );
139  mReport += QLatin1String( "<br>" );
140 
141  const int width = expectedProvider->xSize();
142  const int height = expectedProvider->ySize();
143  std::unique_ptr< QgsRasterBlock > expectedBlock( expectedProvider->block( band, expectedProvider->extent(), width, height ) );
144  std::unique_ptr< QgsRasterBlock > verifiedBlock( verifiedProvider->block( band, expectedProvider->extent(), width, height ) );
145 
146  if ( !expectedBlock || !expectedBlock->isValid() ||
147  !verifiedBlock || !verifiedBlock->isValid() )
148  {
149  allOk = false;
150  mReport += QLatin1String( "cannot read raster block" );
151  continue;
152  }
153 
154  // compare data values
155  QString htmlTable = QStringLiteral( "<table style='%1'>" ).arg( mTabStyle );
156  for ( int row = 0; row < height; row ++ )
157  {
158  htmlTable += QLatin1String( "<tr>" );
159  for ( int col = 0; col < width; col ++ )
160  {
161  bool cellOk = true;
162  const double verifiedVal = verifiedBlock->value( row, col );
163  const double expectedVal = expectedBlock->value( row, col );
164 
165  QString valStr;
166  if ( compare( verifiedVal, expectedVal, 0 ) )
167  {
168  valStr = QString::number( verifiedVal );
169  }
170  else
171  {
172  cellOk = false;
173  allOk = false;
174  valStr = QStringLiteral( "%1<br>%2" ).arg( verifiedVal ).arg( expectedVal );
175  }
176  htmlTable += QStringLiteral( "<td style='%1 %2'>%3</td>" ).arg( mCellStyle, cellOk ? mOkStyle : mErrStyle, valStr );
177  }
178  htmlTable += QLatin1String( "</tr>" );
179  }
180  htmlTable += QLatin1String( "</table>" );
181 
182  mReport += htmlTable;
183  }
184  delete verifiedProvider;
185  delete expectedProvider;
186  return allOk;
187 }
188 
189 void QgsRasterChecker::error( const QString &message, QString &report )
190 {
191  report += QStringLiteral( "<font style='%1'>Error: " ).arg( mErrMsgStyle );
192  report += message;
193  report += QLatin1String( "</font>" );
194 }
195 
196 double QgsRasterChecker::tolerance( double val, int places )
197 {
198  // float precision is about 7 decimal digits, double about 16
199  // default places = 6
200  return 1. * std::pow( 10, std::round( std::log10( std::fabs( val ) ) - places ) );
201 }
202 
203 QString QgsRasterChecker::compareHead()
204 {
205  QString html;
206  html += QStringLiteral( "<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 );
207  return html;
208 }
209 
210 void QgsRasterChecker::compare( const QString &paramName, int verifiedVal, int expectedVal, QString &report, bool &ok )
211 {
212  const bool isEqual = verifiedVal == expectedVal;
213  compareRow( paramName, QString::number( verifiedVal ), QString::number( expectedVal ), report, isEqual, QString::number( verifiedVal - expectedVal ) );
214  if ( !isEqual )
215  ok = false;
216 }
217 
218 void QgsRasterChecker::compare( const QString &paramName, Qgis::DataType verifiedVal, Qgis::DataType expectedVal, QString &report, bool &ok )
219 {
220  const bool isEqual = verifiedVal == expectedVal;
221  compareRow( paramName, QString::number( static_cast< int>( verifiedVal ) ), QString::number( static_cast< int >( expectedVal ) ), report, isEqual, QString::number( static_cast< int >( verifiedVal ) - static_cast< int>( expectedVal ) ) );
222  if ( !isEqual )
223  ok = false;
224 }
225 
226 bool QgsRasterChecker::compare( double verifiedVal, double expectedVal, double tolerance )
227 {
228  // values may be nan
229  return ( std::isnan( verifiedVal ) && std::isnan( expectedVal ) ) || ( std::fabs( verifiedVal - expectedVal ) <= tolerance );
230 }
231 
232 void QgsRasterChecker::compare( const QString &paramName, double verifiedVal, double expectedVal, QString &report, bool &ok, double tolerance )
233 {
234  const bool isNearEqual = compare( verifiedVal, expectedVal, tolerance );
235  compareRow( paramName, QString::number( verifiedVal ), QString::number( expectedVal ), report, isNearEqual, QString::number( verifiedVal - expectedVal ), QString::number( tolerance ) );
236  if ( !isNearEqual )
237  ok = false;
238 }
239 
240 void QgsRasterChecker::compareRow( const QString &paramName, const QString &verifiedVal, const QString &expectedVal, QString &report, bool ok, const QString &difference, const QString &tolerance )
241 {
242  report += QLatin1String( "<tr>\n" );
243  report += QStringLiteral( "<td style='%1'>%2</td><td style='%1 %3'>%4</td><td style='%1'>%5</td>\n" ).arg( mCellStyle, paramName, ok ? mOkStyle : mErrStyle, verifiedVal, expectedVal );
244  report += QStringLiteral( "<td style='%1'>%2</td>\n" ).arg( mCellStyle, difference );
245  report += QStringLiteral( "<td style='%1'>%2</td>\n" ).arg( mCellStyle, tolerance );
246  report += QLatin1String( "</tr>" );
247 }
qgsrasterlayer.h
QgsRasterDataProvider::dataType
Qgis::DataType dataType(int bandNo) const override=0
Returns data type for the band specified by number.
QgsDataProvider::ProviderOptions
Setting options for creating vector data providers.
Definition: qgsdataprovider.h:107
QgsRasterInterface::bandStatistics
virtual QgsRasterBandStats bandStatistics(int bandNo, int stats=QgsRasterBandStats::All, const QgsRectangle &extent=QgsRectangle(), int sampleSize=0, QgsRasterBlockFeedback *feedback=nullptr)
Returns the band statistics.
Definition: qgsrasterinterface.cpp:116
QgsRasterBandStats
The RasterBandStats struct is a container for statistics about a single raster band.
Definition: qgsrasterbandstats.h:34
Qgis::DataType
DataType
Raster data types.
Definition: qgis.h:128
QgsRasterBandStats::mean
double mean
The mean cell value for the band. NO_DATA values are excluded.
Definition: qgsrasterbandstats.h:110
QgsRasterDataProvider::extent
QgsRectangle extent() const override=0
Returns the extent of the layer.
QgsRasterChecker::QgsRasterChecker
QgsRasterChecker()
Definition: qgsrasterchecker.cpp:30
QgsRasterDataProvider::sourceHasNoDataValue
virtual bool sourceHasNoDataValue(int bandNo) const
Returns true if source band has no data value.
Definition: qgsrasterdataprovider.h:241
QgsRasterBandStats::maximumValue
double maximumValue
The maximum cell value in the raster band.
Definition: qgsrasterbandstats.h:101
QgsRasterDataProvider::block
QgsRasterBlock * block(int bandNo, const QgsRectangle &boundingBox, int width, int height, QgsRasterBlockFeedback *feedback=nullptr) override
Read block of data using given extent and size.
Definition: qgsrasterdataprovider.cpp:51
qgsproviderregistry.h
QgsRasterInterface::xSize
virtual int xSize() const
Gets raster size.
Definition: qgsrasterinterface.h:263
QgsRasterDataProvider::sourceNoDataValue
virtual double sourceNoDataValue(int bandNo) const
Value representing no data value.
Definition: qgsrasterdataprovider.h:250
QgsRasterInterface::ySize
virtual int ySize() const
Definition: qgsrasterinterface.h:264
QgsRectangle::toString
QString toString(int precision=16) const
Returns a string representation of form xmin,ymin : xmax,ymax Coordinates will be truncated to the sp...
Definition: qgsrectangle.cpp:127
QgsRasterBandStats::minimumValue
double minimumValue
The minimum cell value in the raster band.
Definition: qgsrasterbandstats.h:107
QgsRasterDataProvider::sourceDataType
Qgis::DataType sourceDataType(int bandNo) const override=0
Returns source data type for the band specified by number, source data type may be shorter than dataT...
QgsRasterChecker::report
QString report() const
Definition: qgsrasterchecker.h:38
QgsRasterBandStats::stdDev
double stdDev
The standard deviation of the cell values.
Definition: qgsrasterbandstats.h:116
QgsRasterInterface::bandCount
virtual int bandCount() const =0
Gets number of bands.
QgsProviderRegistry::instance
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
Definition: qgsproviderregistry.cpp:73
QgsRasterDataProvider
Base class for raster data providers.
Definition: qgsrasterdataprovider.h:88
qgsrasterchecker.h
QgsDataProvider::isValid
virtual bool isValid() const =0
Returns true if this is a valid layer.
QgsRasterChecker::runTest
bool runTest(const QString &verifiedKey, QString verifiedUri, const QString &expectedKey, QString expectedUri)
Test using renderer to generate the image to be compared.
Definition: qgsrasterchecker.cpp:39
qgsrasterdataprovider.h