20 #include <QTextStream>
24 QString QgsRasterLayerUniqueValuesReportAlgorithm::name()
const
26 return QStringLiteral(
"rasterlayeruniquevaluesreport" );
29 QString QgsRasterLayerUniqueValuesReportAlgorithm::displayName()
const
31 return QObject::tr(
"Raster layer unique values report" );
34 QStringList QgsRasterLayerUniqueValuesReportAlgorithm::tags()
const
36 return QObject::tr(
"count,area,statistics" ).split(
',' );
39 QString QgsRasterLayerUniqueValuesReportAlgorithm::group()
const
41 return QObject::tr(
"Raster analysis" );
44 QString QgsRasterLayerUniqueValuesReportAlgorithm::groupId()
const
46 return QStringLiteral(
"rasteranalysis" );
49 void QgsRasterLayerUniqueValuesReportAlgorithm::initAlgorithm(
const QVariantMap & )
52 QObject::tr(
"Input layer" ) ) );
54 QObject::tr(
"Band number" ), 1, QStringLiteral(
"INPUT" ) ) );
56 QObject::tr(
"Unique values report" ), QObject::tr(
"HTML files (*.html)" ), QVariant(),
true ) );
65 addOutput(
new QgsProcessingOutputNumber( QStringLiteral(
"NODATA_PIXEL_COUNT" ), QObject::tr(
"NODATA pixel count" ) ) );
68 QString QgsRasterLayerUniqueValuesReportAlgorithm::shortHelpString()
const
70 return QObject::tr(
"This algorithm returns the count and area of each unique value in a given raster layer." );
73 QgsRasterLayerUniqueValuesReportAlgorithm *QgsRasterLayerUniqueValuesReportAlgorithm::createInstance()
const
75 return new QgsRasterLayerUniqueValuesReportAlgorithm();
80 QgsRasterLayer *layer = parameterAsRasterLayer( parameters, QStringLiteral(
"INPUT" ), context );
81 const int band = parameterAsInt( parameters, QStringLiteral(
"BAND" ), context );
86 mBand = parameterAsInt( parameters, QStringLiteral(
"BAND" ), context );
87 if ( mBand < 1 || mBand > layer->
bandCount() )
88 throw QgsProcessingException( QObject::tr(
"Invalid band number for BAND (%1): Valid values for input raster are 1 to %2" ).arg( mBand )
93 mLayerWidth = layer->
width();
94 mLayerHeight = layer->
height();
106 const QString outputFile = parameterAsFileOutput( parameters, QStringLiteral(
"OUTPUT_HTML_FILE" ), context );
111 std::unique_ptr< QgsFeatureSink > sink;
112 if ( parameters.contains( QStringLiteral(
"OUTPUT_TABLE" ) ) && parameters.value( QStringLiteral(
"OUTPUT_TABLE" ) ).isValid() )
115 outFields.
append(
QgsField( QStringLiteral(
"value" ), QVariant::Double, QString(), 20, 8 ) );
116 outFields.
append(
QgsField( QStringLiteral(
"count" ), QVariant::Int, QString(), 20 ) );
117 outFields.
append(
QgsField( areaUnit.replace( QStringLiteral(
"²" ), QStringLiteral(
"2" ) ), QVariant::Double, QString(), 20, 8 ) );
123 QHash< double, qgssize > uniqueValues;
126 const qgssize layerSize =
static_cast< qgssize >( mLayerWidth ) *
static_cast< qgssize >( mLayerHeight );
129 const int nbBlocksWidth = std::ceil( 1.0 * mLayerWidth / maxWidth );
130 const int nbBlocksHeight = std::ceil( 1.0 * mLayerHeight / maxHeight );
131 const int nbBlocks = nbBlocksWidth * nbBlocksHeight;
134 iter.startRasterRead( mBand, mLayerWidth, mLayerHeight, mExtent );
140 bool isNoData =
false;
141 std::unique_ptr< QgsRasterBlock > rasterBlock;
142 while ( iter.readNextRasterPart( mBand, iterCols, iterRows, rasterBlock, iterLeft, iterTop ) )
144 feedback->
setProgress( 100 * ( ( iterTop / maxHeight * nbBlocksWidth ) + iterLeft / maxWidth ) / nbBlocks );
145 for (
int row = 0; row < iterRows; row++ )
149 for (
int column = 0; column < iterCols; column++ )
151 const double value = rasterBlock->valueAndNoData( row, column, isNoData );
152 if ( mHasNoDataValue && isNoData )
158 uniqueValues[ value ]++;
164 QMap< double, qgssize > sortedUniqueValues;
165 for (
auto it = uniqueValues.constBegin(); it != uniqueValues.constEnd(); ++it )
167 sortedUniqueValues.insert( it.key(), it.value() );
171 outputs.insert( QStringLiteral(
"EXTENT" ), mExtent.toString() );
172 outputs.insert( QStringLiteral(
"CRS_AUTHID" ), mCrs.authid() );
173 outputs.insert( QStringLiteral(
"WIDTH_IN_PIXELS" ), mLayerWidth );
174 outputs.insert( QStringLiteral(
"HEIGHT_IN_PIXELS" ), mLayerHeight );
175 outputs.insert( QStringLiteral(
"TOTAL_PIXEL_COUNT" ), layerSize );
176 outputs.insert( QStringLiteral(
"NODATA_PIXEL_COUNT" ), noDataCount );
178 const double pixelArea = mRasterUnitsPerPixelX * mRasterUnitsPerPixelY;
180 if ( !outputFile.isEmpty() )
182 QFile file( outputFile );
183 if ( file.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
187 QTextStream out( &file );
188 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
189 out.setCodec(
"UTF-8" );
191 out << QStringLiteral(
"<html><head><meta http-equiv=\"Content-Type\" content=\"text/html;charset=utf-8\"/></head><body>\n" );
192 out << QStringLiteral(
"<p>%1: %2 (%3 %4)</p>\n" ).arg( QObject::tr(
"Analyzed file" ), mSource, QObject::tr(
"band" ) ).arg( mBand );
193 out << QObject::tr(
"<p>%1: %2</p>\n" ).arg( QObject::tr(
"Extent" ), mExtent.toString() );
194 out << QObject::tr(
"<p>%1: %2</p>\n" ).arg( QObject::tr(
"Projection" ), mCrs.userFriendlyIdentifier() );
195 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 );
196 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 );
197 out << QObject::tr(
"<p>%1: %2</p>\n" ).arg( QObject::tr(
"Total pixel count" ) ).arg( layerSize );
198 if ( mHasNoDataValue )
199 out << QObject::tr(
"<p>%1: %2</p>\n" ).arg( QObject::tr(
"NODATA pixel count" ) ).arg( noDataCount );
200 out << QStringLiteral(
"<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" ), encodedAreaUnit );
202 for (
auto it = sortedUniqueValues.constBegin(); it != sortedUniqueValues.constEnd(); ++it )
204 const double area = it.value() * pixelArea;
205 out << QStringLiteral(
"<tr><td>%1</td><td>%2</td><td>%3</td></tr>\n" ).arg( it.key() ).arg( it.value() ).arg( QString::number( area,
'g', 16 ) );
207 out << QStringLiteral(
"</table>\n</body></html>" );
208 outputs.insert( QStringLiteral(
"OUTPUT_HTML_FILE" ), outputFile );
214 for (
auto it = sortedUniqueValues.constBegin(); it != sortedUniqueValues.constEnd(); ++it )
217 const double area = it.value() * pixelArea;
220 throw QgsProcessingException( writeFeatureError( sink.get(), parameters, QStringLiteral(
"OUTPUT_TABLE" ) ) );
222 outputs.insert( QStringLiteral(
"OUTPUT_TABLE" ), tableDest );