Skip to content

Commit

Permalink
Generate more descriptive legends for raster layers
Browse files Browse the repository at this point in the history
- include band name details where its useful
- create a legend for multiband raster renderer
  • Loading branch information
nyalldawson committed Dec 16, 2020
1 parent d76ec1c commit 0d7b95f
Show file tree
Hide file tree
Showing 16 changed files with 156 additions and 24 deletions.
Expand Up @@ -121,6 +121,8 @@ Ownership of the enhancement is transferred.

virtual QList<int> usesBands() const;

virtual QList<QgsLayerTreeModelLegendNode *> createLegendNodes( QgsLayerTreeLayer *nodeLayer ) /Factory/;


virtual void toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props = QgsStringMap() ) const;

Expand Down
Expand Up @@ -88,6 +88,8 @@ Returns the raster band used for rendering the raster.

virtual QList< QPair< QString, QColor > > legendSymbologyItems() const;

virtual QList<QgsLayerTreeModelLegendNode *> createLegendNodes( QgsLayerTreeLayer *nodeLayer ) /Factory/;

virtual QList<int> usesBands() const;

virtual void toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props = QgsStringMap() ) const;
Expand Down
Expand Up @@ -148,6 +148,7 @@ Reload data (data could change)

virtual QString colorInterpretationName( int bandNo ) const;


virtual double bandScale( int bandNo ) const;
%Docstring
Read band scale for raster value
Expand Down
14 changes: 14 additions & 0 deletions python/core/auto_generated/raster/qgsrasterinterface.sip.in
Expand Up @@ -223,6 +223,20 @@ Gets raster size
virtual QString generateBandName( int bandNumber ) const;
%Docstring
helper function to create zero padded band names
%End

virtual QString colorInterpretationName( int bandNumber ) const;
%Docstring
Returns the name of the color interpretation for the specified ``bandNumber``.

.. versionadded:: 3.18
%End

QString displayBandName( int bandNumber ) const;
%Docstring
Generates a friendly, descriptive name for the specified ``bandNumber``.

.. versionadded:: 3.18
%End

virtual QgsRasterBlock *block( int bandNo, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback *feedback = 0 ) = 0 /Factory/;
Expand Down
20 changes: 20 additions & 0 deletions src/core/raster/qgsmultibandcolorrenderer.cpp
Expand Up @@ -19,6 +19,7 @@
#include "qgscontrastenhancement.h"
#include "qgsrastertransparency.h"
#include "qgsrasterviewport.h"
#include "qgslayertreemodellegendnode.h"

#include <QDomDocument>
#include <QDomElement>
Expand Down Expand Up @@ -418,6 +419,25 @@ QList<int> QgsMultiBandColorRenderer::usesBands() const
return bandList;
}

QList<QgsLayerTreeModelLegendNode *> QgsMultiBandColorRenderer::createLegendNodes( QgsLayerTreeLayer *nodeLayer )
{
QList<QgsLayerTreeModelLegendNode *> res;
if ( mRedBand != -1 )
{
res << new QgsRasterSymbolLegendNode( nodeLayer, QColor( 255, 0, 0 ), displayBandName( mRedBand ) );
}
if ( mGreenBand != -1 )
{
res << new QgsRasterSymbolLegendNode( nodeLayer, QColor( 0, 255, 0 ), displayBandName( mGreenBand ) );
}
if ( mBlueBand != -1 )
{
res << new QgsRasterSymbolLegendNode( nodeLayer, QColor( 0, 0, 255 ), displayBandName( mBlueBand ) );
}

return res;
}

void QgsMultiBandColorRenderer::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
{
// create base structure
Expand Down
1 change: 1 addition & 0 deletions src/core/raster/qgsmultibandcolorrenderer.h
Expand Up @@ -118,6 +118,7 @@ class CORE_EXPORT QgsMultiBandColorRenderer: public QgsRasterRenderer
void writeXml( QDomDocument &doc, QDomElement &parentElem ) const override;

QList<int> usesBands() const override;
QList<QgsLayerTreeModelLegendNode *> createLegendNodes( QgsLayerTreeLayer *nodeLayer ) SIP_FACTORY override;

void toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props = QgsStringMap() ) const override;

Expand Down
23 changes: 23 additions & 0 deletions src/core/raster/qgspalettedrasterrenderer.cpp
Expand Up @@ -22,6 +22,7 @@
#include "qgsstyleentityvisitor.h"
#include "qgsmessagelog.h"
#include "qgsrasteriterator.h"
#include "qgslayertreemodellegendnode.h"

#include <QColor>
#include <QDomDocument>
Expand Down Expand Up @@ -328,6 +329,28 @@ QList< QPair< QString, QColor > > QgsPalettedRasterRenderer::legendSymbologyItem
return symbolItems;
}


QList<QgsLayerTreeModelLegendNode *> QgsPalettedRasterRenderer::createLegendNodes( QgsLayerTreeLayer *nodeLayer )
{
QList<QgsLayerTreeModelLegendNode *> res;

const QString name = displayBandName( mBand );
if ( !name.isEmpty() )
{
res << new QgsSimpleLegendNode( nodeLayer, name );
}

const QList< QPair< QString, QColor > > items = legendSymbologyItems();
res.reserve( res.size() + items.size() );
for ( const QPair< QString, QColor > &item : items )
{
res << new QgsRasterSymbolLegendNode( nodeLayer, item.second, item.first );
}

return res;
}


QList<int> QgsPalettedRasterRenderer::usesBands() const
{
QList<int> bandList;
Expand Down
1 change: 1 addition & 0 deletions src/core/raster/qgspalettedrasterrenderer.h
Expand Up @@ -101,6 +101,7 @@ class CORE_EXPORT QgsPalettedRasterRenderer: public QgsRasterRenderer

void writeXml( QDomDocument &doc, QDomElement &parentElem ) const override;
QList< QPair< QString, QColor > > legendSymbologyItems() const override;
QList<QgsLayerTreeModelLegendNode *> createLegendNodes( QgsLayerTreeLayer *nodeLayer ) SIP_FACTORY override;
QList<int> usesBands() const override;
void toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props = QgsStringMap() ) const override;
bool accept( QgsStyleEntityVisitorInterface *visitor ) const override;
Expand Down
12 changes: 10 additions & 2 deletions src/core/raster/qgsrasterdataprovider.cpp
Expand Up @@ -247,6 +247,12 @@ QgsRasterDataProvider::ProviderCapabilities QgsRasterDataProvider::providerCapab
return QgsRasterDataProvider::NoProviderCapabilities;
}

int QgsRasterDataProvider::colorInterpretation( int bandNo ) const
{
Q_UNUSED( bandNo )
return QgsRaster::UndefinedColorInterpretation;
}

//
//Random Static convenience function
//
Expand Down Expand Up @@ -624,5 +630,7 @@ void QgsRasterDataProvider::writeXml( QDomDocument &doc, QDomElement &parentElem
QString::number( mMaxOversampling ) );
}


// ENDS
QString QgsRasterDataProvider::colorInterpretationName( int bandNo ) const
{
return colorName( colorInterpretation( bandNo ) );
}
11 changes: 2 additions & 9 deletions src/core/raster/qgsrasterdataprovider.h
Expand Up @@ -152,11 +152,7 @@ class CORE_EXPORT QgsRasterDataProvider : public QgsDataProvider, public QgsRast
Qgis::DataType sourceDataType( int bandNo ) const override = 0;

//! Returns data type for the band specified by number
virtual int colorInterpretation( int bandNo ) const
{
Q_UNUSED( bandNo )
return QgsRaster::UndefinedColorInterpretation;
}
virtual int colorInterpretation( int bandNo ) const;

QString colorName( int colorInterpretation ) const
{
Expand Down Expand Up @@ -221,10 +217,7 @@ class CORE_EXPORT QgsRasterDataProvider : public QgsDataProvider, public QgsRast
//! Reload data (data could change)
virtual bool reload() { return true; }

virtual QString colorInterpretationName( int bandNo ) const
{
return colorName( colorInterpretation( bandNo ) );
}
QString colorInterpretationName( int bandNo ) const override;

/**
* Read band scale for raster value
Expand Down
27 changes: 27 additions & 0 deletions src/core/raster/qgsrasterinterface.cpp
Expand Up @@ -616,3 +616,30 @@ QString QgsRasterInterface::capabilitiesString() const

return abilitiesList.join( QLatin1String( ", " ) );
}

QString QgsRasterInterface::generateBandName( int bandNumber ) const
{
if ( mInput )
return mInput->generateBandName( bandNumber );

return tr( "Band" ) + QStringLiteral( " %1" ) .arg( bandNumber, 1 + static_cast< int >( std::log10( static_cast< double >( bandCount() ) ) ), 10, QChar( '0' ) );
}

QString QgsRasterInterface::colorInterpretationName( int bandNo ) const
{
if ( mInput )
return mInput->colorInterpretationName( bandNo );

return QString();
}

QString QgsRasterInterface::displayBandName( int bandNumber ) const
{
QString name = generateBandName( bandNumber );
QString colorInterp = colorInterpretationName( bandNumber );
if ( colorInterp != QLatin1String( "Undefined" ) )
{
name.append( QStringLiteral( " (%1)" ).arg( colorInterp ) );
}
return name;
}
19 changes: 15 additions & 4 deletions src/core/raster/qgsrasterinterface.h
Expand Up @@ -242,10 +242,21 @@ class CORE_EXPORT QgsRasterInterface
virtual int ySize() const { return mInput ? mInput->ySize() : 0; }

//! \brief helper function to create zero padded band names
virtual QString generateBandName( int bandNumber ) const
{
return tr( "Band" ) + QStringLiteral( " %1" ) .arg( bandNumber, 1 + static_cast< int >( std::log10( static_cast< double >( bandCount() ) ) ), 10, QChar( '0' ) );
}
virtual QString generateBandName( int bandNumber ) const;

/**
* Returns the name of the color interpretation for the specified \a bandNumber.
*
* \since QGIS 3.18
*/
virtual QString colorInterpretationName( int bandNumber ) const;

/**
* Generates a friendly, descriptive name for the specified \a bandNumber.
*
* \since QGIS 3.18
*/
QString displayBandName( int bandNumber ) const;

/**
* Read block of data using given extent and size.
Expand Down
6 changes: 6 additions & 0 deletions src/core/raster/qgssinglebandgrayrenderer.cpp
Expand Up @@ -217,6 +217,12 @@ QList<QgsLayerTreeModelLegendNode *> QgsSingleBandGrayRenderer::createLegendNode
QList<QgsLayerTreeModelLegendNode *> res;
if ( mContrastEnhancement && mContrastEnhancement->contrastEnhancementAlgorithm() != QgsContrastEnhancement::NoEnhancement )
{
const QString name = displayBandName( mGrayBand );
if ( !name.isEmpty() )
{
res << new QgsSimpleLegendNode( nodeLayer, name );
}

const QColor minColor = ( mGradient == BlackToWhite ) ? Qt::black : Qt::white;
const QColor maxColor = ( mGradient == BlackToWhite ) ? Qt::white : Qt::black;
res << new QgsColorRampLegendNode( nodeLayer, new QgsGradientColorRamp( minColor, maxColor ),
Expand Down
7 changes: 7 additions & 0 deletions src/core/raster/qgssinglebandpseudocolorrenderer.cpp
Expand Up @@ -431,6 +431,13 @@ QList<QgsLayerTreeModelLegendNode *> QgsSingleBandPseudoColorRenderer::createLeg
return QList<QgsLayerTreeModelLegendNode *>();

QList<QgsLayerTreeModelLegendNode *> res;

const QString name = displayBandName( mBand );
if ( !name.isEmpty() )
{
res << new QgsSimpleLegendNode( nodeLayer, name );
}

switch ( rampShader->colorRampType() )
{
case QgsColorRampShader::Interpolated:
Expand Down
9 changes: 1 addition & 8 deletions src/gui/raster/qgsrasterbandcombobox.cpp
Expand Up @@ -166,12 +166,5 @@ QString QgsRasterBandComboBox::displayBandName( QgsRasterDataProvider *provider,
if ( !provider )
return QString();

QString name = provider->generateBandName( band );

QString colorInterp = provider->colorInterpretationName( band );
if ( colorInterp != QLatin1String( "Undefined" ) )
{
name.append( QStringLiteral( " (%1)" ).arg( colorInterp ) );
}
return name;
return provider->displayBandName( band );
}
25 changes: 24 additions & 1 deletion tests/src/core/testqgslegendrenderer.cpp
Expand Up @@ -119,7 +119,30 @@ static bool _verifyImage( const QString &testName, QString &report, int diff = 3
return equal;
}

class TestRasterRenderer : public QgsPalettedRasterRenderer
{
public:

TestRasterRenderer( QgsRasterInterface *input, int bandNumber, const ClassData &classes )
: QgsPalettedRasterRenderer( input, bandNumber, classes )
{}

// don't create the default legend nodes for this layer!
QList<QgsLayerTreeModelLegendNode *> createLegendNodes( QgsLayerTreeLayer *nodeLayer ) override
{
QList<QgsLayerTreeModelLegendNode *> res;

const QList< QPair< QString, QColor > > items = legendSymbologyItems();
res.reserve( res.size() + items.size() );
for ( const QPair< QString, QColor > &item : items )
{
res << new QgsRasterSymbolLegendNode( nodeLayer, item.second, item.first );
}

return res;
}

};

class TestQgsLegendRenderer : public QObject
{
Expand Down Expand Up @@ -263,7 +286,7 @@ void TestQgsLegendRenderer::init()
QString rasterUri = QStringLiteral( "MEM:::DATAPOINTER=%1,PIXELS=2,LINES=2" ).arg( ( qulonglong ) RASTER_ARRAY );
mRL = new QgsRasterLayer( rasterUri, QStringLiteral( "Raster Layer" ), QStringLiteral( "gdal" ) );

std::unique_ptr< QgsPalettedRasterRenderer > rasterRenderer( new QgsPalettedRasterRenderer( mRL->dataProvider(), 1,
std::unique_ptr< TestRasterRenderer > rasterRenderer( new TestRasterRenderer( mRL->dataProvider(), 1,
{
QgsPalettedRasterRenderer::Class( 1, QColor( 0, 0, 0 ), QStringLiteral( "1" ) ),
QgsPalettedRasterRenderer::Class( 2, QColor( 255, 255, 255 ), QStringLiteral( "2" ) )
Expand Down

0 comments on commit 0d7b95f

Please sign in to comment.