From 83f3927d873900783c8018031db16f1123aec80e Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Thu, 16 Sep 2021 12:31:36 +0200 Subject: [PATCH] Don't crash collecting stats when band is not valid unreported Cherry-picked from master 6039deda1b. --- src/core/raster/qgspalettedrasterrenderer.cpp | 196 +++++++++--------- 1 file changed, 100 insertions(+), 96 deletions(-) diff --git a/src/core/raster/qgspalettedrasterrenderer.cpp b/src/core/raster/qgspalettedrasterrenderer.cpp index e0abd90f8ae3..d636ed7959b7 100644 --- a/src/core/raster/qgspalettedrasterrenderer.cpp +++ b/src/core/raster/qgspalettedrasterrenderer.cpp @@ -476,135 +476,139 @@ QgsPalettedRasterRenderer::ClassData QgsPalettedRasterRenderer::classDataFromRas return ClassData(); ClassData data; - qlonglong numClasses = 0; - if ( feedback ) - feedback->setProgress( 0 ); - - // Collect unique values for float rasters - if ( raster->dataType( bandNumber ) == Qgis::DataType::Float32 || raster->dataType( bandNumber ) == Qgis::DataType::Float64 ) + if ( bandNumber > 0 && bandNumber <= raster->bandCount() ) { + qlonglong numClasses = 0; - if ( feedback && feedback->isCanceled() ) - { - return data; - } + if ( feedback ) + feedback->setProgress( 0 ); - std::set values; + // Collect unique values for float rasters + if ( raster->dataType( bandNumber ) == Qgis::DataType::Float32 || raster->dataType( bandNumber ) == Qgis::DataType::Float64 ) + { - int maxWidth = QgsRasterIterator::DEFAULT_MAXIMUM_TILE_WIDTH; - int maxHeight = QgsRasterIterator::DEFAULT_MAXIMUM_TILE_HEIGHT; + if ( feedback && feedback->isCanceled() ) + { + return data; + } - QgsRasterIterator iter( raster ); - iter.startRasterRead( bandNumber, raster->xSize(), raster->ySize(), raster->extent(), feedback ); + std::set values; - int nbBlocksWidth = static_cast< int >( std::ceil( 1.0 * raster->xSize() / maxWidth ) ); - int nbBlocksHeight = static_cast< int >( std::ceil( 1.0 * raster->ySize() / maxHeight ) ); - int nbBlocks = nbBlocksWidth * nbBlocksHeight; + const int maxWidth = QgsRasterIterator::DEFAULT_MAXIMUM_TILE_WIDTH; + const int maxHeight = QgsRasterIterator::DEFAULT_MAXIMUM_TILE_HEIGHT; - int iterLeft = 0; - int iterTop = 0; - int iterCols = 0; - int iterRows = 0; - std::unique_ptr< QgsRasterBlock > rasterBlock; - QgsRectangle blockExtent; - bool isNoData = false; - while ( iter.readNextRasterPart( bandNumber, iterCols, iterRows, rasterBlock, iterLeft, iterTop, &blockExtent ) ) - { - if ( feedback ) - feedback->setProgress( 100 * ( ( iterTop / maxHeight * nbBlocksWidth ) + iterLeft / maxWidth ) / nbBlocks ); + QgsRasterIterator iter( raster ); + iter.startRasterRead( bandNumber, raster->xSize(), raster->ySize(), raster->extent(), feedback ); - if ( feedback && feedback->isCanceled() ) - break; + const int nbBlocksWidth = static_cast< int >( std::ceil( 1.0 * raster->xSize() / maxWidth ) ); + const int nbBlocksHeight = static_cast< int >( std::ceil( 1.0 * raster->ySize() / maxHeight ) ); + const int nbBlocks = nbBlocksWidth * nbBlocksHeight; - for ( int row = 0; row < iterRows; row++ ) + int iterLeft = 0; + int iterTop = 0; + int iterCols = 0; + int iterRows = 0; + std::unique_ptr< QgsRasterBlock > rasterBlock; + QgsRectangle blockExtent; + bool isNoData = false; + while ( iter.readNextRasterPart( bandNumber, iterCols, iterRows, rasterBlock, iterLeft, iterTop, &blockExtent ) ) { + if ( feedback ) + feedback->setProgress( 100 * ( ( iterTop / maxHeight * nbBlocksWidth ) + iterLeft / maxWidth ) / nbBlocks ); + if ( feedback && feedback->isCanceled() ) break; - for ( int column = 0; column < iterCols; column++ ) + for ( int row = 0; row < iterRows; row++ ) { if ( feedback && feedback->isCanceled() ) break; - const double currentValue = rasterBlock->valueAndNoData( row, column, isNoData ); - if ( numClasses >= MAX_FLOAT_CLASSES ) - { - QgsMessageLog::logMessage( QStringLiteral( "Number of classes exceeded maximum (%1)." ).arg( MAX_FLOAT_CLASSES ), QStringLiteral( "Raster" ) ); - break; - } - if ( !isNoData && values.find( currentValue ) == values.end() ) + for ( int column = 0; column < iterCols; column++ ) { - values.insert( currentValue ); - data.push_back( Class( currentValue, QColor(), QLocale().toString( currentValue ) ) ); - numClasses++; + if ( feedback && feedback->isCanceled() ) + break; + + const double currentValue = rasterBlock->valueAndNoData( row, column, isNoData ); + if ( numClasses >= MAX_FLOAT_CLASSES ) + { + QgsMessageLog::logMessage( QStringLiteral( "Number of classes exceeded maximum (%1)." ).arg( MAX_FLOAT_CLASSES ), QStringLiteral( "Raster" ) ); + break; + } + if ( !isNoData && values.find( currentValue ) == values.end() ) + { + values.insert( currentValue ); + data.push_back( Class( currentValue, QColor(), QLocale().toString( currentValue ) ) ); + numClasses++; + } } } } + // must be sorted + std::sort( data.begin(), data.end(), []( const Class & a, const Class & b ) -> bool + { + return a.value < b.value; + } ); } - // must be sorted - std::sort( data.begin(), data.end(), []( const Class & a, const Class & b ) -> bool - { - return a.value < b.value; - } ); - } - else - { - // get min and max value from raster - QgsRasterBandStats stats = raster->bandStatistics( bandNumber, QgsRasterBandStats::Min | QgsRasterBandStats::Max, QgsRectangle(), 0, feedback ); - if ( feedback && feedback->isCanceled() ) - return ClassData(); - - double min = stats.minimumValue; - double max = stats.maximumValue; - // need count of every individual value - int bins = std::ceil( max - min ) + 1; - if ( bins <= 0 ) - return ClassData(); - - QgsRasterHistogram histogram = raster->histogram( bandNumber, bins, min, max, QgsRectangle(), 0, false, feedback ); - if ( feedback && feedback->isCanceled() ) - return ClassData(); - - double interval = ( histogram.maximum - histogram.minimum + 1 ) / histogram.binCount; - double currentValue = histogram.minimum; - for ( int idx = 0; idx < histogram.binCount; ++idx ) + else { - int count = histogram.histogramVector.at( idx ); - if ( count > 0 ) + // get min and max value from raster + const QgsRasterBandStats stats = raster->bandStatistics( bandNumber, QgsRasterBandStats::Min | QgsRasterBandStats::Max, QgsRectangle(), 0, feedback ); + if ( feedback && feedback->isCanceled() ) + return ClassData(); + + const double min = stats.minimumValue; + const double max = stats.maximumValue; + // need count of every individual value + const int bins = std::ceil( max - min ) + 1; + if ( bins <= 0 ) + return ClassData(); + + const QgsRasterHistogram histogram = raster->histogram( bandNumber, bins, min, max, QgsRectangle(), 0, false, feedback ); + if ( feedback && feedback->isCanceled() ) + return ClassData(); + + const double interval = ( histogram.maximum - histogram.minimum + 1 ) / histogram.binCount; + double currentValue = histogram.minimum; + for ( int idx = 0; idx < histogram.binCount; ++idx ) { - data << Class( currentValue, QColor(), QLocale().toString( currentValue ) ); - numClasses++; + const int count = histogram.histogramVector.at( idx ); + if ( count > 0 ) + { + data << Class( currentValue, QColor(), QLocale().toString( currentValue ) ); + numClasses++; + } + currentValue += interval; } - currentValue += interval; } - } - // assign colors from ramp - if ( ramp && numClasses > 0 ) - { - int i = 0; - - if ( QgsRandomColorRamp *randomRamp = dynamic_cast( ramp ) ) + // assign colors from ramp + if ( ramp && numClasses > 0 ) { - //ramp is a random colors ramp, so inform it of the total number of required colors - //this allows the ramp to pregenerate a set of visually distinctive colors - randomRamp->setTotalColorCount( data.count() ); - } + int i = 0; - if ( numClasses > 1 ) - numClasses -= 1; //avoid duplicate first color + if ( QgsRandomColorRamp *randomRamp = dynamic_cast( ramp ) ) + { + //ramp is a random colors ramp, so inform it of the total number of required colors + //this allows the ramp to pregenerate a set of visually distinctive colors + randomRamp->setTotalColorCount( data.count() ); + } - QgsPalettedRasterRenderer::ClassData::iterator cIt = data.begin(); - for ( ; cIt != data.end(); ++cIt ) - { - if ( feedback ) + if ( numClasses > 1 ) + numClasses -= 1; //avoid duplicate first color + + QgsPalettedRasterRenderer::ClassData::iterator cIt = data.begin(); + for ( ; cIt != data.end(); ++cIt ) { - // Show no less than 1%, then the max between class fill and real progress - feedback->setProgress( std::max( 1, 100 * ( i + 1 ) / numClasses ) ); + if ( feedback ) + { + // Show no less than 1%, then the max between class fill and real progress + feedback->setProgress( std::max( 1, 100 * ( i + 1 ) / numClasses ) ); + } + cIt->color = ramp->color( i / static_cast( numClasses ) ); + i++; } - cIt->color = ramp->color( i / static_cast( numClasses ) ); - i++; } } return data;