QGIS API Documentation  3.0.2-Girona (307d082)
qgsalgorithmrasterlayeruniquevalues.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsalgorithmrasterlayeruniquevalues.cpp
3  ---------------------
4  begin : April 2017
5  copyright : (C) 2017 by Nyall Dawson
6  email : nyall dot dawson at gmail dot com
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
19 
21 
22 QString QgsRasterLayerUniqueValuesReportAlgorithm::name() const
23 {
24  return QStringLiteral( "rasterlayeruniquevaluesreport" );
25 }
26 
27 QString QgsRasterLayerUniqueValuesReportAlgorithm::displayName() const
28 {
29  return QObject::tr( "Raster layer unique values report" );
30 }
31 
32 QStringList QgsRasterLayerUniqueValuesReportAlgorithm::tags() const
33 {
34  return QObject::tr( "count,area,statistics" ).split( ',' );
35 }
36 
37 QString QgsRasterLayerUniqueValuesReportAlgorithm::group() const
38 {
39  return QObject::tr( "Raster analysis" );
40 }
41 
42 QString QgsRasterLayerUniqueValuesReportAlgorithm::groupId() const
43 {
44  return QStringLiteral( "rasteranalysis" );
45 }
46 
47 void QgsRasterLayerUniqueValuesReportAlgorithm::initAlgorithm( const QVariantMap & )
48 {
49  addParameter( new QgsProcessingParameterRasterLayer( QStringLiteral( "INPUT" ),
50  QObject::tr( "Input layer" ) ) );
51  addParameter( new QgsProcessingParameterBand( QStringLiteral( "BAND" ),
52  QObject::tr( "Band number" ), 1, QStringLiteral( "INPUT" ) ) );
53  addParameter( new QgsProcessingParameterFileDestination( QStringLiteral( "OUTPUT_HTML_FILE" ),
54  QObject::tr( "Unique values report" ), QObject::tr( "HTML files (*.html)" ), QVariant(), true ) );
55 
56  addOutput( new QgsProcessingOutputString( QStringLiteral( "EXTENT" ), QObject::tr( "Extent" ) ) );
57  addOutput( new QgsProcessingOutputString( QStringLiteral( "CRS_AUTHID" ), QObject::tr( "CRS authority identifier" ) ) );
58  addOutput( new QgsProcessingOutputNumber( QStringLiteral( "WIDTH_IN_PIXELS" ), QObject::tr( "Width in pixels" ) ) );
59  addOutput( new QgsProcessingOutputNumber( QStringLiteral( "HEIGHT_IN_PIXELS" ), QObject::tr( "Height in pixels" ) ) );
60  addOutput( new QgsProcessingOutputNumber( QStringLiteral( "TOTAL_PIXEL_COUNT" ), QObject::tr( "Total pixel count" ) ) );
61  addOutput( new QgsProcessingOutputNumber( QStringLiteral( "NODATA_PIXEL_COUNT" ), QObject::tr( "NODATA pixel count" ) ) );
62 }
63 
64 QString QgsRasterLayerUniqueValuesReportAlgorithm::shortHelpString() const
65 {
66  return QObject::tr( "This algorithm returns the count and area of each unique value in a given raster layer." );
67 }
68 
69 QgsRasterLayerUniqueValuesReportAlgorithm *QgsRasterLayerUniqueValuesReportAlgorithm::createInstance() const
70 {
71  return new QgsRasterLayerUniqueValuesReportAlgorithm();
72 }
73 
74 bool QgsRasterLayerUniqueValuesReportAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback * )
75 {
76  QgsRasterLayer *layer = parameterAsRasterLayer( parameters, QStringLiteral( "INPUT" ), context );
77  int band = parameterAsInt( parameters, QStringLiteral( "BAND" ), context );
78 
79  if ( !layer )
80  return false;
81 
82  mInterface.reset( layer->dataProvider()->clone() );
83  mHasNoDataValue = layer->dataProvider()->sourceHasNoDataValue( band );
84  mLayerWidth = layer->width();
85  mLayerHeight = layer->height();
86  mExtent = layer->extent();
87  mCrs = layer->crs();
88  mRasterUnitsPerPixelX = layer->rasterUnitsPerPixelX();
89  mRasterUnitsPerPixelY = layer->rasterUnitsPerPixelY();
90  mSource = layer->source();
91 
92  return true;
93 }
94 
95 QVariantMap QgsRasterLayerUniqueValuesReportAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
96 {
97  int band = parameterAsInt( parameters, QStringLiteral( "BAND" ), context );
98  QString outputFile = parameterAsFileOutput( parameters, QStringLiteral( "OUTPUT_HTML_FILE" ), context );
99 
100  QHash< double, qgssize > uniqueValues;
101  qgssize noDataCount = 0;
102 
103  qgssize layerSize = static_cast< qgssize >( mLayerWidth ) * static_cast< qgssize >( mLayerHeight );
104  int maxWidth = 4000;
105  int maxHeight = 4000;
106  int nbBlocksWidth = std::ceil( 1.0 * mLayerWidth / maxWidth );
107  int nbBlocksHeight = std::ceil( 1.0 * mLayerHeight / maxHeight );
108  int nbBlocks = nbBlocksWidth * nbBlocksHeight;
109 
110  QgsRasterIterator iter( mInterface.get() );
111  iter.setMaximumTileWidth( maxWidth );
112  iter.setMaximumTileHeight( maxHeight );
113  iter.startRasterRead( band, mLayerWidth, mLayerHeight, mExtent );
114 
115  int iterLeft = 0;
116  int iterTop = 0;
117  int iterCols = 0;
118  int iterRows = 0;
119  QgsRasterBlock *rasterBlock = nullptr;
120  while ( iter.readNextRasterPart( band, iterCols, iterRows, &rasterBlock, iterLeft, iterTop ) )
121  {
122  feedback->setProgress( 100 * ( ( iterTop / maxHeight * nbBlocksWidth ) + iterLeft / maxWidth ) / nbBlocks );
123  for ( int row = 0; row < iterRows; row++ )
124  {
125  if ( feedback->isCanceled() )
126  break;
127  for ( int column = 0; column < iterCols; column++ )
128  {
129  if ( mHasNoDataValue && rasterBlock->isNoData( row, column ) )
130  {
131  noDataCount += 1;
132  }
133  else
134  {
135  double value = rasterBlock->value( row, column );
136  uniqueValues[ value ]++;
137  }
138  }
139  }
140  delete rasterBlock;
141  }
142 
143  QMap< double, qgssize > sortedUniqueValues;
144  for ( auto it = uniqueValues.constBegin(); it != uniqueValues.constEnd(); ++it )
145  {
146  sortedUniqueValues.insert( it.key(), it.value() );
147  }
148 
149  QVariantMap outputs;
150  outputs.insert( QStringLiteral( "EXTENT" ), mExtent.toString() );
151  outputs.insert( QStringLiteral( "CRS_AUTHID" ), mCrs.authid() );
152  outputs.insert( QStringLiteral( "WIDTH_IN_PIXELS" ), mLayerWidth );
153  outputs.insert( QStringLiteral( "HEIGHT_IN_PIXELS" ), mLayerHeight );
154  outputs.insert( QStringLiteral( "TOTAL_PIXEL_COUNT" ), layerSize );
155  outputs.insert( QStringLiteral( "NODATA_PIXEL_COUNT" ), noDataCount );
156 
157  if ( !outputFile.isEmpty() )
158  {
159  QFile file( outputFile );
160  if ( file.open( QIODevice::WriteOnly | QIODevice::Text ) )
161  {
162  QString areaUnit = QgsUnitTypes::toAbbreviatedString( QgsUnitTypes::distanceToAreaUnit( mCrs.mapUnits() ) );
163  double pixelArea = mRasterUnitsPerPixelX * mRasterUnitsPerPixelY;
164 
165  QTextStream out( &file );
166  out << QString( "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html;charset=utf-8\"/></head><body>\n" );
167  out << QString( "<p>%1: %2 (%3 %4)</p>\n" ).arg( QObject::tr( "Analyzed file" ), mSource, QObject::tr( "band" ) ).arg( band );
168  out << QObject::tr( "<p>%1: %2</p>\n" ).arg( QObject::tr( "Extent" ), mExtent.toString() );
169  out << QObject::tr( "<p>%1: %2 (%3)</p>\n" ).arg( QObject::tr( "Projection" ), mCrs.description(), mCrs.authid() );
170  out << QObject::tr( "<p>%1: %2 (%3 %4)</p>\n" ).arg( QObject::tr( "Width in pixels" ) ).arg( mLayerWidth ).arg( QObject::tr( "units per pixel" ) ).arg( mRasterUnitsPerPixelX );
171  out << QObject::tr( "<p>%1: %2 (%3 %4)</p>\n" ).arg( QObject::tr( "Height in pixels" ) ).arg( mLayerHeight ).arg( QObject::tr( "units per pixel" ) ).arg( mRasterUnitsPerPixelY );
172  out << QObject::tr( "<p>%1: %2</p>\n" ).arg( QObject::tr( "Total pixel count" ) ).arg( layerSize );
173  if ( mHasNoDataValue )
174  out << QObject::tr( "<p>%1: %2</p>\n" ).arg( QObject::tr( "NODATA pixel count" ) ).arg( noDataCount );
175  out << QString( "<table><tr><td>%1</td><td>%2</td><td>%3 (%4)</td></tr>\n" ).arg( QObject::tr( "Value" ), QObject::tr( "Pixel count" ), QObject::tr( "Area" ), areaUnit );
176 
177  for ( auto it = sortedUniqueValues.constBegin(); it != sortedUniqueValues.constEnd(); ++it )
178  {
179  double area = it.value() * pixelArea;
180  out << QString( "<tr><td>%1</td><td>%2</td><td>%3</td></tr>\n" ).arg( it.key() ).arg( it.value() ).arg( QString::number( area, 'g', 16 ) );
181  }
182  out << QString( "</table>\n</body></html>" );
183  outputs.insert( QStringLiteral( "OUTPUT_HTML_FILE" ), outputFile );
184  }
185  }
186 
187  return outputs;
188 }
189 
190 
192 
193 
194 
int width() const
Accessor that returns the width of the (unclipped) raster.
Base class for providing feedback from a processing algorithm.
Iterator for sequentially processing raster cells.
This class provides qgis with the ability to render raster datasets onto the mapcanvas.
double rasterUnitsPerPixelX() const
Returns the number of raster units per each raster pixel in X axis. In a world file, this is normally the first row (without the sign)
QgsRasterInterface * clone() const override=0
Clone itself, create deep copy.
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition: qgsfeedback.h:63
double rasterUnitsPerPixelY() const
Returns the number of raster units per each raster pixel in Y axis. In a world file, this is normally the first row (without the sign)
A numeric output for processing algorithms.
A raster band parameter for Processing algorithms.
int height() const
Accessor that returns the height of the (unclipped) raster.
bool isNoData(int row, int column)
Check if value at position is no data.
virtual QgsRectangle extent() const
Returns the extent of the layer.
A string output for processing algorithms.
QgsRasterDataProvider * dataProvider() override
A raster layer parameter for processing algorithms.
static Q_INVOKABLE QString toAbbreviatedString(QgsUnitTypes::DistanceUnit unit)
Returns a translated abbreviation representing a distance unit.
Raster data container.
QgsCoordinateReferenceSystem crs() const
Returns the layer&#39;s spatial reference system.
A generic file based destination parameter, for specifying the destination path for a file (non-map l...
virtual bool sourceHasNoDataValue(int bandNo) const
Return true if source band has no data value.
unsigned long long qgssize
Qgssize is used instead of size_t, because size_t is stdlib type, unknown by SIP, and it would be har...
Definition: qgis.h:488
void setMaximumTileWidth(int w)
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:54
QString source() const
Returns the source for the layer.
double value(int row, int column) const
Read a single value if type of block is numeric.
Contains information about the context in which a processing algorithm is executed.
static Q_INVOKABLE QgsUnitTypes::AreaUnit distanceToAreaUnit(QgsUnitTypes::DistanceUnit distanceUnit)
Converts a distance unit to its corresponding area unit, e.g., meters to square meters.