Skip to content
Permalink
Browse files
Refactor QgsPalettedRasterRenderer to allow "gaps" in pixel values
Previously the renderer required that pixel values followed
sequential numerical order. This refactor allows values to
be skipped, so that only certain color index will be rendered.
  • Loading branch information
nyalldawson committed Apr 3, 2017
1 parent 620a839 commit a157db7627abba28f4da6d4cf57a75e28938dc28
@@ -1536,7 +1536,10 @@ QgsPaintEffectRegistry {#qgis_api_break_3_0_QgsPaintEffectRegistry}
QgsPalettedRasterRenderer {#qgis_api_break_3_0_QgsPalettedRasterRenderer}
-------------------------

- The rgbArray() method was made private
- The rgbArray() method was removed
- colors() has been removed, use classes() instead.
- The constructor has a different signature and requires a list of classes instead of an array


QgsPalLabeling {#qgis_api_break_3_0_QgsPalLabeling}
--------------
@@ -4,8 +4,17 @@ class QgsPalettedRasterRenderer : QgsRasterRenderer
#include "qgspalettedrasterrenderer.h"
%End
public:
/** Renderer owns color array*/
QgsPalettedRasterRenderer( QgsRasterInterface* input, int bandNumber, QColor* colorArray /Array,Transfer/, int nColors /ArraySize/, const QVector<QString>& labels = QVector<QString>() );

struct Class
{

Class( const QColor &color, const QString &label );
QColor color;
QString label;
};

typedef QMap< int, QgsPalettedRasterRenderer::Class > ClassData;
QgsPalettedRasterRenderer( QgsRasterInterface *input, int bandNumber, const ClassData &classes );
~QgsPalettedRasterRenderer();
virtual QgsPalettedRasterRenderer * clone() const /Factory/;
static QgsRasterRenderer* create( const QDomElement& elem, QgsRasterInterface* input ) /Factory/;
@@ -14,20 +23,13 @@ class QgsPalettedRasterRenderer : QgsRasterRenderer

/** Returns number of colors*/
int nColors() const;
/** Returns copy of color array (caller takes ownership)*/
QColor* colors() const /Factory/;

/** Return optional category label
* @note added in 2.1 */
ClassData classes() const;
QString label( int idx ) const;
void setLabel( int idx, const QString &label );

/** Set category label
* @note added in 2.1 */
void setLabel( int idx, const QString& label );

void writeXml( QDomDocument& doc, QDomElement& parentElem ) const;
void writeXml( QDomDocument &doc, QDomElement &parentElem ) const;

void legendSymbologyItems( QList< QPair< QString, QColor > >& symbolItems /Out/ ) const;
void legendSymbologyItems( QList< QPair< QString, QColor > > &symbolItems /Out/ ) const;

QList<int> usesBands() const;

@@ -26,34 +26,24 @@
#include <QVector>
#include <memory>

QgsPalettedRasterRenderer::QgsPalettedRasterRenderer( QgsRasterInterface *input, int bandNumber,
QColor *colorArray, int nColors, const QVector<QString> &labels ):
QgsRasterRenderer( input, QStringLiteral( "paletted" ) ), mBand( bandNumber ), mNColors( nColors ), mLabels( labels )
{
mColors = new QRgb[nColors];
for ( int i = 0; i < nColors; ++i )
{
mColors[i] = qPremultiply( colorArray[i].rgba() );
}
delete[] colorArray;
}

QgsPalettedRasterRenderer::QgsPalettedRasterRenderer( QgsRasterInterface *input, int bandNumber, QRgb *colorArray, int nColors, const QVector<QString> &labels ):
QgsRasterRenderer( input, QStringLiteral( "paletted" ) ), mBand( bandNumber ), mColors( colorArray ), mNColors( nColors ), mLabels( labels )
QgsPalettedRasterRenderer::QgsPalettedRasterRenderer( QgsRasterInterface *input, int bandNumber, const ClassData &classes )
: QgsRasterRenderer( input, QStringLiteral( "paletted" ) )
, mBand( bandNumber )
, mClassData( classes )
{
updateArrays();
}

QgsPalettedRasterRenderer::~QgsPalettedRasterRenderer()
{
delete[] mColors;
delete[] mIsNoData;
}

QgsPalettedRasterRenderer *QgsPalettedRasterRenderer::clone() const
{
QgsPalettedRasterRenderer *renderer = new QgsPalettedRasterRenderer( nullptr, mBand, rgbArray(), mNColors );
QgsPalettedRasterRenderer *renderer = new QgsPalettedRasterRenderer( nullptr, mBand, mClassData );
renderer->copyCommonProperties( this );

renderer->mLabels = mLabels;
return renderer;
}

@@ -65,9 +55,7 @@ QgsRasterRenderer *QgsPalettedRasterRenderer::create( const QDomElement &elem, Q
}

int bandNumber = elem.attribute( QStringLiteral( "band" ), QStringLiteral( "-1" ) ).toInt();
int nColors = 0;
QRgb *colors = nullptr;
QVector<QString> labels;
ClassData classData;

QDomElement paletteElem = elem.firstChildElement( QStringLiteral( "colorPalette" ) );
if ( !paletteElem.isNull() )
@@ -76,89 +64,50 @@ QgsRasterRenderer *QgsPalettedRasterRenderer::create( const QDomElement &elem, Q

QDomElement entryElem;
int value;
nColors = 0;

// We cannot believe that data are correct, check first max value
for ( int i = 0; i < paletteEntries.size(); ++i )
{
entryElem = paletteEntries.at( i ).toElement();
// Could be written as doubles (with .0000) in old project files
value = ( int )entryElem.attribute( QStringLiteral( "value" ), QStringLiteral( "0" ) ).toDouble();
if ( value >= nColors && value <= 10000 ) nColors = value + 1;
}
QgsDebugMsgLevel( QString( "nColors = %1" ).arg( nColors ), 4 );

colors = new QRgb[ nColors ];

for ( int i = 0; i < nColors; ++i )
{
QColor color;
QString label;
entryElem = paletteEntries.at( i ).toElement();
value = ( int )entryElem.attribute( QStringLiteral( "value" ), QStringLiteral( "0" ) ).toDouble();
QgsDebugMsgLevel( entryElem.attribute( "color", "#000000" ), 4 );
if ( value >= 0 && value < nColors )
{
QColor color = QColor( entryElem.attribute( QStringLiteral( "color" ), QStringLiteral( "#000000" ) ) );
color.setAlpha( entryElem.attribute( QStringLiteral( "alpha" ), QStringLiteral( "255" ) ).toInt() );
colors[value] = qPremultiply( color.rgba() );
QString label = entryElem.attribute( QStringLiteral( "label" ) );
if ( !label.isEmpty() )
{
if ( value >= labels.size() ) labels.resize( value + 1 );
labels[value] = label;
}
}
else
{
QgsDebugMsg( QString( "value %1 out of range" ).arg( value ) );
}
color = QColor( entryElem.attribute( QStringLiteral( "color" ), QStringLiteral( "#000000" ) ) );
color.setAlpha( entryElem.attribute( QStringLiteral( "alpha" ), QStringLiteral( "255" ) ).toInt() );
label = entryElem.attribute( QStringLiteral( "label" ) );
classData.insert( value, Class( color, label ) );
}
}
QgsPalettedRasterRenderer *r = new QgsPalettedRasterRenderer( input, bandNumber, colors, nColors, labels );
QgsPalettedRasterRenderer *r = new QgsPalettedRasterRenderer( input, bandNumber, classData );
r->readXml( elem );
return r;
}

QColor *QgsPalettedRasterRenderer::colors() const
QgsPalettedRasterRenderer::ClassData QgsPalettedRasterRenderer::classes() const
{
if ( mNColors < 1 )
{
return nullptr;
}
QColor *colorArray = new QColor[ mNColors ];
for ( int i = 0; i < mNColors; ++i )
{
colorArray[i] = QColor::fromRgba( qUnpremultiply( mColors[i] ) );
}
return colorArray;
return mClassData;
}

QRgb *QgsPalettedRasterRenderer::rgbArray() const
QString QgsPalettedRasterRenderer::label( int idx ) const
{
if ( mNColors < 1 )
{
return nullptr;
}
QRgb *rgbValues = new QRgb[mNColors];
for ( int i = 0; i < mNColors; ++i )
{
rgbValues[i] = mColors[i];
}
return rgbValues;
if ( !mClassData.contains( idx ) )
return QString();

return mClassData.value( idx ).label;
}

void QgsPalettedRasterRenderer::setLabel( int idx, const QString &label )
{
if ( idx >= mLabels.size() )
{
mLabels.resize( idx + 1 );
}
mLabels[idx] = label;
if ( !mClassData.contains( idx ) )
return;

mClassData[ idx ].label = label;
}

QgsRasterBlock *QgsPalettedRasterRenderer::block( int bandNo, QgsRectangle const &extent, int width, int height, QgsRasterBlockFeedback *feedback )
{
std::unique_ptr< QgsRasterBlock > outputBlock( new QgsRasterBlock() );
if ( !mInput || mNColors == 0 )
if ( !mInput || mClassData.isEmpty() )
{
return outputBlock.release();
}
@@ -211,6 +160,11 @@ QgsRasterBlock *QgsPalettedRasterRenderer::block( int bandNo, QgsRectangle cons
continue;
}
int val = ( int ) inputBlock->value( i );
if ( val > mMaxColorIndex || mIsNoData[ val ] )
{
outputData[i] = myDefaultColor;
continue;
}

if ( !hasTransparency )
{
@@ -225,7 +179,7 @@ QgsRasterBlock *QgsPalettedRasterRenderer::block( int bandNo, QgsRectangle cons
}
if ( mAlphaBand > 0 )
{
currentOpacity *= alphaBlock->value( i ) / 255.0;
currentOpacity *= alphaBlock->value( i ) / 255.0;
}

QRgb c = mColors[val];
@@ -248,16 +202,17 @@ void QgsPalettedRasterRenderer::writeXml( QDomDocument &doc, QDomElement &parent

rasterRendererElem.setAttribute( QStringLiteral( "band" ), mBand );
QDomElement colorPaletteElem = doc.createElement( QStringLiteral( "colorPalette" ) );
for ( int i = 0; i < mNColors; ++i )
ClassData::const_iterator it = mClassData.constBegin();
for ( ; it != mClassData.constEnd(); ++it )
{
QColor color = QColor::fromRgba( qUnpremultiply( mColors[i] ) );
QColor color = it.value().color;
QDomElement colorElem = doc.createElement( QStringLiteral( "paletteEntry" ) );
colorElem.setAttribute( QStringLiteral( "value" ), i );
colorElem.setAttribute( QStringLiteral( "value" ), it.key() );
colorElem.setAttribute( QStringLiteral( "color" ), color.name() );
colorElem.setAttribute( QStringLiteral( "alpha" ), color.alpha() );
if ( !label( i ).isEmpty() )
if ( !it.value().label.isEmpty() )
{
colorElem.setAttribute( QStringLiteral( "label" ), label( i ) );
colorElem.setAttribute( QStringLiteral( "label" ), it.value().label );
}
colorPaletteElem.appendChild( colorElem );
}
@@ -268,10 +223,25 @@ void QgsPalettedRasterRenderer::writeXml( QDomDocument &doc, QDomElement &parent

void QgsPalettedRasterRenderer::legendSymbologyItems( QList< QPair< QString, QColor > > &symbolItems ) const
{
for ( int i = 0; i < mNColors; ++i )
QList< QPair< int, QPair< QString, QColor > > > legends;
ClassData::const_iterator it = mClassData.constBegin();
for ( ; it != mClassData.constEnd(); ++it )
{
QString lab = it.value().label.isEmpty() ? QString::number( it.key() ) : it.value().label;
legends.push_back( qMakePair( it.key(), qMakePair( lab, it.value().color ) ) );
}

// sort by color value
std::sort( legends.begin(), legends.end(),
[]( const QPair< int, QPair< QString, QColor > > &a, const QPair< int, QPair< QString, QColor > > &b ) -> bool
{
return a.first < b.first;
} );

QList< QPair< int, QPair< QString, QColor > > >::const_iterator lIt = legends.constBegin();
for ( ; lIt != legends.constEnd(); ++lIt )
{
QString lab = label( i ).isEmpty() ? QString::number( i ) : label( i );
symbolItems.push_back( qMakePair( lab, QColor::fromRgba( qUnpremultiply( mColors[i] ) ) ) );
symbolItems << lIt->second;
}
}

@@ -284,3 +254,28 @@ QList<int> QgsPalettedRasterRenderer::usesBands() const
}
return bandList;
}

void QgsPalettedRasterRenderer::updateArrays()
{
// find maximum color index
ClassData::const_iterator mIt = mClassData.constBegin();
for ( ; mIt != mClassData.constEnd(); ++mIt )
{
mMaxColorIndex = qMax( mMaxColorIndex, mIt.key() );
}

delete [] mColors;
delete [] mIsNoData;
mColors = new QRgb[mMaxColorIndex + 1];
mIsNoData = new bool[mMaxColorIndex + 1];
std::fill( mIsNoData, mIsNoData + mMaxColorIndex, true );

int i = 0;
ClassData::const_iterator it = mClassData.constBegin();
for ( ; it != mClassData.constEnd(); ++it )
{
mColors[it.key()] = qPremultiply( it.value().color.rgba() );
mIsNoData[it.key()] = false;
i++;
}
}
@@ -33,9 +33,28 @@ class CORE_EXPORT QgsPalettedRasterRenderer: public QgsRasterRenderer
{
public:

//! Renderer owns color array
QgsPalettedRasterRenderer( QgsRasterInterface *input, int bandNumber, QColor *colorArray, int nColors, const QVector<QString> &labels = QVector<QString>() );
QgsPalettedRasterRenderer( QgsRasterInterface *input, int bandNumber, QRgb *colorArray, int nColors, const QVector<QString> &labels = QVector<QString>() );
//! Properties of a single value class
struct Class
{
//! Constructor for Class
Class( const QColor &color = QColor(), const QString &label = QString() )
: color( color )
, label( label )
{}

//! Color to render value
QColor color;
//! Label for value
QString label;
};

//! Map of value to class properties
typedef QMap< int, Class > ClassData;

/**
* Constructor for QgsPalettedRasterRenderer.
*/
QgsPalettedRasterRenderer( QgsRasterInterface *input, int bandNumber, const ClassData &classes );
~QgsPalettedRasterRenderer();

//! QgsPalettedRasterRenderer cannot be copied. Use clone() instead.
@@ -49,13 +68,16 @@ class CORE_EXPORT QgsPalettedRasterRenderer: public QgsRasterRenderer
QgsRasterBlock *block( int bandNo, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback *feedback = nullptr ) override;

//! Returns number of colors
int nColors() const { return mNColors; }
//! Returns copy of color array (caller takes ownership)
QColor *colors() const;
int nColors() const { return mClassData.size(); }

/**
* Returns a map of value to classes (colors) used by the renderer.
*/
ClassData classes() const;

/** Return optional category label
* \since QGIS 2.1 */
QString label( int idx ) const { return mLabels.value( idx ); }
* \since QGIS 2.1 */
QString label( int idx ) const;

/** Set category label
* \since QGIS 2.1 */
@@ -69,17 +91,16 @@ class CORE_EXPORT QgsPalettedRasterRenderer: public QgsRasterRenderer

private:

/** Returns copy of premultiplied rgb array (caller takes ownership)
*/
QRgb *rgbArray() const;

int mBand;
int mMaxColorIndex = -INT_MAX;
ClassData mClassData;


//! Premultiplied color array
QRgb *mColors = nullptr;
//! Number of colors
int mNColors;
//! Optional category labels, size of vector may be < mNColors
QVector<QString> mLabels;
bool *mIsNoData = nullptr;
void updateArrays();


};

Loading

0 comments on commit a157db7

Please sign in to comment.