Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Raster paletted/unique float support #39088

Merged
merged 4 commits into from Oct 5, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -24,12 +24,12 @@ Renderer for paletted raster images.

struct Class
{
Class( int value, const QColor &color = QColor(), const QString &label = QString() );
Class( double value, const QColor &color = QColor(), const QString &label = QString() );
%Docstring
Constructor for Class
%End

int value;
double value;

QColor color;
QString label;
Expand Down Expand Up @@ -63,14 +63,14 @@ Returns number of colors
Returns a map of value to classes (colors) used by the renderer.
%End

QString label( int idx ) const;
QString label( double idx ) const;
%Docstring
Returns optional category label

.. versionadded:: 2.1
%End

void setLabel( int idx, const QString &label );
void setLabel( double idx, const QString &label );
%Docstring
Set category label

Expand Down
3 changes: 2 additions & 1 deletion src/core/providers/gdal/qgsgdalprovider.cpp
Expand Up @@ -683,8 +683,9 @@ QgsRasterBlock *QgsGdalProvider::block( int bandNo, const QgsRectangle &extent,
}
if ( !readBlock( bandNo, extent, width, height, block->bits(), feedback ) )
{
QgsDebugMsg( QStringLiteral( "Error occurred while reading block" ) );
block->setError( { tr( "Error occurred while reading block." ), QStringLiteral( "GDAL" ) } );
block->setIsNoData();
block->setValid( false );
return block.release();
}
// apply scale and offset
Expand Down
144 changes: 110 additions & 34 deletions src/core/raster/qgspalettedrasterrenderer.cpp
Expand Up @@ -20,13 +20,18 @@
#include "qgsrasterviewport.h"
#include "qgssymbollayerutils.h"
#include "qgsstyleentityvisitor.h"
#include "qgsmessagelog.h"
#include "qgsrasteriterator.h"

#include <QColor>
#include <QDomDocument>
#include <QDomElement>
#include <QImage>
#include <QVector>
#include <memory>
#include <set>

const int QgsPalettedRasterRenderer::MAX_FLOAT_CLASSES = 65536;

QgsPalettedRasterRenderer::QgsPalettedRasterRenderer( QgsRasterInterface *input, int bandNumber, const ClassData &classes )
: QgsRasterRenderer( input, QStringLiteral( "paletted" ) )
Expand Down Expand Up @@ -69,7 +74,7 @@ QgsRasterRenderer *QgsPalettedRasterRenderer::create( const QDomElement &elem, Q
QColor color;
QString label;
entryElem = paletteEntries.at( i ).toElement();
value = static_cast<int>( entryElem.attribute( QStringLiteral( "value" ), QStringLiteral( "0" ) ).toDouble() );
value = entryElem.attribute( QStringLiteral( "value" ), QStringLiteral( "0" ) ).toDouble();
color = QColor( entryElem.attribute( QStringLiteral( "color" ), QStringLiteral( "#000000" ) ) );
color.setAlpha( entryElem.attribute( QStringLiteral( "alpha" ), QStringLiteral( "255" ) ).toInt() );
label = entryElem.attribute( QStringLiteral( "label" ) );
Expand All @@ -96,7 +101,7 @@ QgsPalettedRasterRenderer::ClassData QgsPalettedRasterRenderer::classes() const
return mClassData;
}

QString QgsPalettedRasterRenderer::label( int idx ) const
QString QgsPalettedRasterRenderer::label( double idx ) const
{
const auto constMClassData = mClassData;
for ( const Class &c : constMClassData )
Expand All @@ -108,7 +113,7 @@ QString QgsPalettedRasterRenderer::label( int idx ) const
return QString();
}

void QgsPalettedRasterRenderer::setLabel( int idx, const QString &label )
void QgsPalettedRasterRenderer::setLabel( double idx, const QString &label )
{
ClassData::iterator cIt = mClassData.begin();
for ( ; cIt != mClassData.end(); ++cIt )
Expand Down Expand Up @@ -179,30 +184,29 @@ QgsRasterBlock *QgsPalettedRasterRenderer::block( int, QgsRectangle const &exte
outputData[i] = myDefaultColor;
continue;
}
int val = static_cast< int >( value );
if ( !mColors.contains( val ) )
if ( !mColors.contains( value ) )
{
outputData[i] = myDefaultColor;
continue;
}

if ( !hasTransparency )
{
outputData[i] = mColors.value( val );
outputData[i] = mColors.value( value );
}
else
{
currentOpacity = mOpacity;
if ( mRasterTransparency )
{
currentOpacity = mRasterTransparency->alphaValue( val, mOpacity * 255 ) / 255.0;
currentOpacity = mRasterTransparency->alphaValue( value, mOpacity * 255 ) / 255.0;
}
if ( mAlphaBand > 0 )
{
currentOpacity *= alphaBlock->value( i ) / 255.0;
}

QRgb c = mColors.value( val );
QRgb c = mColors.value( value );
outputData[i] = qRgba( currentOpacity * qRed( c ), currentOpacity * qGreen( c ), currentOpacity * qBlue( c ), currentOpacity * qAlpha( c ) );
}
}
Expand Down Expand Up @@ -472,37 +476,104 @@ QgsPalettedRasterRenderer::ClassData QgsPalettedRasterRenderer::classDataFromRas
if ( !raster )
return ClassData();

// 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();
ClassData data;
qlonglong numClasses = 0;

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();
if ( feedback )
feedback->setProgress( 0 );

QgsRasterHistogram histogram = raster->histogram( bandNumber, bins, min, max, QgsRectangle(), 0, false, feedback );
if ( feedback && feedback->isCanceled() )
return ClassData();
// Collect unique values for float rasters
if ( raster->dataType( bandNumber ) == Qgis::DataType::Float32 || raster->dataType( bandNumber ) == Qgis::DataType::Float64 )
{

if ( feedback && feedback->isCanceled() )
{
return data;
}

double interval = ( histogram.maximum - histogram.minimum + 1 ) / histogram.binCount;
std::set<double> values;

ClassData data;
int maxWidth = QgsRasterIterator::DEFAULT_MAXIMUM_TILE_WIDTH;
int maxHeight = QgsRasterIterator::DEFAULT_MAXIMUM_TILE_HEIGHT;

QgsRasterIterator iter( raster );
iter.startRasterRead( bandNumber, raster->xSize(), raster->ySize(), raster->extent(), feedback );

int nbBlocksWidth = static_cast< int >( std::ceil( 1.0 * raster->ySize() / maxWidth ) );
int nbBlocksHeight = static_cast< int >( std::ceil( 1.0 * raster->ySize() / maxHeight ) );
int nbBlocks = nbBlocksWidth * nbBlocksHeight;

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 );

double currentValue = histogram.minimum;
double presentValues = 0;
for ( int idx = 0; idx < histogram.binCount; ++idx )
if ( feedback && feedback->isCanceled() )
break;

for ( int row = 0; row < iterRows; row++ )
{
if ( feedback && feedback->isCanceled() )
break;

for ( int column = 0; column < iterCols; column++ )
{
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++;
}
}
}
}
}
else
{
int count = histogram.histogramVector.at( idx );
if ( count > 0 )
// 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 )
{
data << Class( currentValue, QColor(), QLocale().toString( currentValue ) );
presentValues++;
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
Expand All @@ -517,13 +588,18 @@ QgsPalettedRasterRenderer::ClassData QgsPalettedRasterRenderer::classDataFromRas
randomRamp->setTotalColorCount( data.count() );
}

if ( presentValues > 1 )
presentValues -= 1; //avoid duplicate first color
if ( numClasses > 1 )
numClasses -= 1; //avoid duplicate first color

QgsPalettedRasterRenderer::ClassData::iterator cIt = data.begin();
for ( ; cIt != data.end(); ++cIt )
{
cIt->color = ramp->color( i / presentValues );
if ( feedback )
{
// Show no less than 1%, then the max between class fill and real progress
feedback->setProgress( std::max<int>( 1, 100 * ( i + 1 ) / numClasses ) );
}
cIt->color = ramp->color( i / static_cast<double>( numClasses ) );
i++;
}
}
Expand Down
13 changes: 8 additions & 5 deletions src/core/raster/qgspalettedrasterrenderer.h
Expand Up @@ -40,14 +40,14 @@ class CORE_EXPORT QgsPalettedRasterRenderer: public QgsRasterRenderer
struct Class
{
//! Constructor for Class
Class( int value, const QColor &color = QColor(), const QString &label = QString() )
Class( double value, const QColor &color = QColor(), const QString &label = QString() )
: value( value )
, color( color )
, label( label )
{}

//! Value
int value;
double value;

//! Color to render value
QColor color;
Expand Down Expand Up @@ -85,13 +85,13 @@ class CORE_EXPORT QgsPalettedRasterRenderer: public QgsRasterRenderer
* Returns optional category label
* \since QGIS 2.1
*/
QString label( int idx ) const;
QString label( double idx ) const;

/**
* Set category label
* \since QGIS 2.1
*/
void setLabel( int idx, const QString &label );
void setLabel( double idx, const QString &label );

/**
* Returns the raster band used for rendering the raster.
Expand Down Expand Up @@ -169,8 +169,11 @@ class CORE_EXPORT QgsPalettedRasterRenderer: public QgsRasterRenderer
std::unique_ptr<QgsColorRamp> mSourceColorRamp;

//! Premultiplied color map
QMap< int, QRgb > mColors;
QMap< double, QRgb > mColors;
void updateArrays();

// Maximum number of allowed classes for float rasters
static const int MAX_FLOAT_CLASSES;
};

#endif // QGSPALETTEDRASTERRENDERER_H
13 changes: 13 additions & 0 deletions src/core/raster/qgsrasterdataprovider.cpp
Expand Up @@ -21,6 +21,7 @@
#include "qgsprovidermetadata.h"
#include "qgsrasterprojector.h"
#include "qgslogger.h"
#include "qgsmessagelog.h"
#include "qgsapplication.h"

#include <QTime>
Expand Down Expand Up @@ -56,6 +57,8 @@ QgsRasterBlock *QgsRasterDataProvider::block( int bandNo, QgsRectangle const &b
if ( block->isEmpty() )
{
QgsDebugMsg( QStringLiteral( "Couldn't create raster block" ) );
block->setError( { tr( "Couldn't create raster block." ), QStringLiteral( "Raster" ) } );
block->setValid( false );
return block.release();
}

Expand All @@ -65,6 +68,8 @@ QgsRasterBlock *QgsRasterDataProvider::block( int bandNo, QgsRectangle const &b
if ( tmpExtent.isEmpty() )
{
QgsDebugMsg( QStringLiteral( "Extent outside provider extent" ) );
block->setError( { tr( "Extent outside provider extent." ), QStringLiteral( "Raster" ) } );
block->setValid( false );
block->setIsNoData();
return block.release();
}
Expand Down Expand Up @@ -113,6 +118,8 @@ QgsRasterBlock *QgsRasterDataProvider::block( int bandNo, QgsRectangle const &b
{
// Should not happen
QgsDebugMsg( QStringLiteral( "Row or column limits out of range" ) );
block->setError( { tr( "Row or column limits out of range" ), QStringLiteral( "Raster" ) } );
block->setValid( false );
return block.release();
}

Expand Down Expand Up @@ -149,6 +156,8 @@ QgsRasterBlock *QgsRasterDataProvider::block( int bandNo, QgsRectangle const &b
if ( !readBlock( bandNo, tmpExtent, tmpWidth, tmpHeight, tmpBlock->bits(), feedback ) )
{
QgsDebugMsg( QStringLiteral( "Error occurred while reading block" ) );
block->setError( { tr( "Error occurred while reading block." ), QStringLiteral( "Raster" ) } );
block->setValid( false );
block->setIsNoData();
return block.release();
}
Expand All @@ -174,6 +183,8 @@ QgsRasterBlock *QgsRasterDataProvider::block( int bandNo, QgsRectangle const &b
{
QgsDebugMsg( QStringLiteral( "Source row or column limits out of range" ) );
block->setIsNoData(); // so that the problem becomes obvious and fixed
block->setError( { tr( "Source row or column limits out of range." ), QStringLiteral( "Raster" ) } );
block->setValid( false );
return block.release();
}

Expand Down Expand Up @@ -202,6 +213,8 @@ QgsRasterBlock *QgsRasterDataProvider::block( int bandNo, QgsRectangle const &b
{
QgsDebugMsg( QStringLiteral( "Error occurred while reading block" ) );
block->setIsNoData();
block->setError( { tr( "Error occurred while reading block." ), QStringLiteral( "Raster" ) } );
block->setValid( false );
return block.release();
}
}
Expand Down