[FEATURE] Add a color ramp combo to paletted raster renderer

Allows rapid recoloring of palette to match a color ramp
This commit is contained in:
Nyall Dawson 2017-03-23 15:54:13 +10:00
parent 40027abcd2
commit bfe96214b2
7 changed files with 197 additions and 33 deletions

View File

@ -32,6 +32,8 @@ class QgsPalettedRasterRenderer : QgsRasterRenderer
void legendSymbologyItems( QList< QPair< QString, QColor > > &symbolItems /Out/ ) const;
QList<int> usesBands() const;
void setSourceColorRamp( QgsColorRamp *ramp /Transfer/ );
QgsColorRamp *sourceColorRamp() const;
static QgsPalettedRasterRenderer::ClassData colorTableToClassData( const QList<QgsColorRampShader::ColorRampItem> &table );
private:

View File

@ -18,6 +18,7 @@
#include "qgspalettedrasterrenderer.h"
#include "qgsrastertransparency.h"
#include "qgsrasterviewport.h"
#include "qgssymbollayerutils.h"
#include <QColor>
#include <QDomDocument>
@ -43,6 +44,9 @@ QgsPalettedRasterRenderer::~QgsPalettedRasterRenderer()
QgsPalettedRasterRenderer *QgsPalettedRasterRenderer::clone() const
{
QgsPalettedRasterRenderer *renderer = new QgsPalettedRasterRenderer( nullptr, mBand, mClassData );
if ( mSourceColorRamp )
renderer->setSourceColorRamp( mSourceColorRamp->clone() );
renderer->copyCommonProperties( this );
return renderer;
}
@ -78,8 +82,17 @@ QgsRasterRenderer *QgsPalettedRasterRenderer::create( const QDomElement &elem, Q
classData.insert( value, Class( color, label ) );
}
}
QgsPalettedRasterRenderer *r = new QgsPalettedRasterRenderer( input, bandNumber, classData );
r->readXml( elem );
// try to load color ramp (optional)
QDomElement sourceColorRampElem = elem.firstChildElement( QStringLiteral( "colorramp" ) );
if ( !sourceColorRampElem.isNull() && sourceColorRampElem.attribute( QStringLiteral( "name" ) ) == QLatin1String( "[source]" ) )
{
r->setSourceColorRamp( QgsSymbolLayerUtils::loadColorRamp( sourceColorRampElem ) );
}
return r;
}
@ -218,6 +231,13 @@ void QgsPalettedRasterRenderer::writeXml( QDomDocument &doc, QDomElement &parent
}
rasterRendererElem.appendChild( colorPaletteElem );
// save source color ramp
if ( mSourceColorRamp )
{
QDomElement colorRampElem = QgsSymbolLayerUtils::saveColorRamp( QStringLiteral( "[source]" ), mSourceColorRamp.get(), doc );
rasterRendererElem.appendChild( colorRampElem );
}
parentElem.appendChild( rasterRendererElem );
}
@ -255,6 +275,16 @@ QList<int> QgsPalettedRasterRenderer::usesBands() const
return bandList;
}
void QgsPalettedRasterRenderer::setSourceColorRamp( QgsColorRamp *ramp )
{
mSourceColorRamp.reset( ramp );
}
QgsColorRamp *QgsPalettedRasterRenderer::sourceColorRamp() const
{
return mSourceColorRamp.get();
}
QgsPalettedRasterRenderer::ClassData QgsPalettedRasterRenderer::colorTableToClassData( const QList<QgsColorRampShader::ColorRampItem> &table )
{
QList<QgsColorRampShader::ColorRampItem>::const_iterator colorIt = table.constBegin();

View File

@ -90,6 +90,19 @@ class CORE_EXPORT QgsPalettedRasterRenderer: public QgsRasterRenderer
QList<int> usesBands() const override;
/**
* Set the source color \a ramp. Ownership is transferred to the renderer.
* @note added in QGIS 3.0
* @see sourceColorRamp()
*/
void setSourceColorRamp( QgsColorRamp *ramp );
/** Get the source color ramp
* @note added in QGIS 3.0
* @see setSourceColorRamp()
*/
QgsColorRamp *sourceColorRamp() const;
/**
* Converts a raster color \a table to paletted renderer class data.
* @note added in QGIS 3.0
@ -102,6 +115,8 @@ class CORE_EXPORT QgsPalettedRasterRenderer: public QgsRasterRenderer
int mMaxColorIndex = -INT_MAX;
ClassData mClassData;
//! Source color ramp
std::unique_ptr<QgsColorRamp> mSourceColorRamp;
//! Premultiplied color array
QRgb *mColors = nullptr;

View File

@ -20,6 +20,7 @@
#include "qgsrasterdataprovider.h"
#include "qgsrasterlayer.h"
#include "qgscolordialog.h"
#include "qgssettings.h"
#include <QColorDialog>
#include <QInputDialog>
@ -47,6 +48,11 @@ QgsPalettedRendererWidget::QgsPalettedRendererWidget( QgsRasterLayer *layer, con
connect( mTreeView, &QTreeView::customContextMenuRequested, [ = ]( const QPoint & ) { contextMenu->exec( QCursor::pos() ); }
);
QgsSettings settings;
QString defaultPalette = settings.value( QStringLiteral( "Raster/defaultPalette" ), QString() ).toString();
btnColorRamp->setColorRampFromName( defaultPalette );
connect( btnColorRamp, &QgsColorRampButton::colorRampChanged, this, &QgsPalettedRendererWidget::applyColorRamp );
if ( mRasterLayer )
{
QgsRasterDataProvider *provider = mRasterLayer->dataProvider();
@ -75,7 +81,13 @@ QgsRasterRenderer *QgsPalettedRendererWidget::renderer()
{
QgsPalettedRasterRenderer::ClassData classes = mModel->classData();
int bandNumber = mBandComboBox->currentData().toInt();
return new QgsPalettedRasterRenderer( mRasterLayer->dataProvider(), bandNumber, classes );
QgsPalettedRasterRenderer *r = new QgsPalettedRasterRenderer( mRasterLayer->dataProvider(), bandNumber, classes );
if ( !btnColorRamp->isNull() )
{
r->setSourceColorRamp( btnColorRamp->colorRamp() );
}
return r;
}
void QgsPalettedRendererWidget::setFromRenderer( const QgsRasterRenderer *r )
@ -85,6 +97,17 @@ void QgsPalettedRendererWidget::setFromRenderer( const QgsRasterRenderer *r )
{
//read values and colors and fill into tree widget
mModel->setClassData( pr->classes() );
if ( pr->sourceColorRamp() )
{
whileBlocking( btnColorRamp )->setColorRamp( pr->sourceColorRamp() );
}
else
{
QgsSettings settings;
QString defaultPalette = settings.value( "/Raster/defaultPalette", "Spectral" ).toString();
whileBlocking( btnColorRamp )->setColorRampFromName( defaultPalette );
}
}
else
{
@ -95,6 +118,9 @@ void QgsPalettedRendererWidget::setFromRenderer( const QgsRasterRenderer *r )
QgsPalettedRasterRenderer::ClassData classes = QgsPalettedRasterRenderer::colorTableToClassData( provider->colorTable( mBandComboBox->currentData().toInt() ) );
mModel->setClassData( classes );
}
QgsSettings settings;
QString defaultPalette = settings.value( "/Raster/defaultPalette", "Spectral" ).toString();
whileBlocking( btnColorRamp )->setColorRampFromName( defaultPalette );
}
}
@ -137,7 +163,14 @@ void QgsPalettedRendererWidget::deleteEntry()
void QgsPalettedRendererWidget::addEntry()
{
disconnect( mModel, &QgsPalettedRendererModel::classesChanged, this, &QgsPalettedRendererWidget::widgetChanged );
mModel->insertRow( mModel->rowCount() );
QColor color( 150, 150, 150 );
std::unique_ptr< QgsColorRamp > ramp( btnColorRamp->colorRamp() );
if ( ramp )
{
color = ramp->color( 1.0 );
}
mModel->addEntry( color );
connect( mModel, &QgsPalettedRendererModel::classesChanged, this, &QgsPalettedRendererWidget::widgetChanged );
emit widgetChanged();
}
@ -231,6 +264,39 @@ void QgsPalettedRendererWidget::changeLabel()
}
}
void QgsPalettedRendererWidget::applyColorRamp()
{
std::unique_ptr< QgsColorRamp > ramp( btnColorRamp->colorRamp() );
if ( !ramp )
{
return;
}
if ( !btnColorRamp->colorRampName().isEmpty() )
{
// Remember last used color ramp
QgsSettings settings;
settings.setValue( QStringLiteral( "Raster/defaultPalette" ), btnColorRamp->colorRampName() );
}
disconnect( mModel, &QgsPalettedRendererModel::classesChanged, this, &QgsPalettedRendererWidget::widgetChanged );
QgsPalettedRasterRenderer::ClassData data = mModel->classData();
QgsPalettedRasterRenderer::ClassData::iterator cIt = data.begin();
double numberOfEntries = data.count();
int i = 0;
for ( ; cIt != data.end(); ++cIt )
{
cIt->color = ramp->color( i / numberOfEntries );
i++;
}
mModel->setClassData( data );
connect( mModel, &QgsPalettedRendererModel::classesChanged, this, &QgsPalettedRendererWidget::widgetChanged );
emit widgetChanged();
}
//
// QgsPalettedRendererModel
//
@ -465,5 +531,11 @@ bool QgsPalettedRendererModel::insertRows( int row, int count, const QModelIndex
return true;
}
void QgsPalettedRendererModel::addEntry( const QColor &color )
{
insertRow( rowCount() );
setData( index( mData.count() - 1, 1 ), color );
}
///@endcond PRIVATE

View File

@ -55,6 +55,8 @@ class QgsPalettedRendererModel : public QAbstractTableModel
bool removeRows( int row, int count, const QModelIndex &parent = QModelIndex() ) override;
virtual bool insertRows( int row, int count, const QModelIndex &parent = QModelIndex() ) override;
void addEntry( const QColor &color );
signals:
void classesChanged();
@ -98,6 +100,7 @@ class GUI_EXPORT QgsPalettedRendererWidget: public QgsRasterRendererWidget, priv
void changeColor();
void changeTransparency();
void changeLabel();
void applyColorRamp();
};
#endif // QGSPALETTEDRENDERERWIDGET_H

View File

@ -26,7 +26,59 @@
<property name="bottomMargin">
<number>3</number>
</property>
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="mBandLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Band</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="mBandComboBox"/>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QTreeView" name="mTreeView">
<property name="minimumSize">
<size>
<width>0</width>
<height>280</height>
</size>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QgsColorRampButton" name="btnColorRamp">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>120</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
<item row="3" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QPushButton" name="mAddEntryButton">
@ -65,38 +117,16 @@
</item>
</layout>
</item>
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="mBandLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Band</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="mBandComboBox"/>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QTreeView" name="mTreeView">
<property name="minimumSize">
<size>
<width>0</width>
<height>280</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QgsColorRampButton</class>
<extends>QToolButton</extends>
<header>qgscolorrampbutton.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>mBandComboBox</tabstop>
<tabstop>mTreeView</tabstop>

View File

@ -34,7 +34,8 @@ from qgis.core import (QgsRaster,
QgsRenderChecker,
QgsPalettedRasterRenderer,
QgsSingleBandGrayRenderer,
QgsSingleBandPseudoColorRenderer)
QgsSingleBandPseudoColorRenderer,
QgsLimitedRandomColorRamp)
from utilities import unitTestDataPath
from qgis.testing import start_app, unittest
@ -329,6 +330,12 @@ class TestQgsRasterLayer(unittest.TestCase):
renderer.setLabel(3, 'new class')
self.assertEqual(renderer.label(3), 'new class')
# color ramp
r = QgsLimitedRandomColorRamp(5)
renderer.setSourceColorRamp(r)
self.assertEqual(renderer.sourceColorRamp().type(), 'random')
self.assertEqual(renderer.sourceColorRamp().count(), 5)
# clone
new_renderer = renderer.clone()
classes = new_renderer.classes()
@ -336,6 +343,8 @@ class TestQgsRasterLayer(unittest.TestCase):
self.assertEqual(classes[3].label, 'new class')
self.assertEqual(classes[1].color.name(), '#00ff00')
self.assertEqual(classes[3].color.name(), '#ff0000')
self.assertEqual(new_renderer.sourceColorRamp().type(), 'random')
self.assertEqual(new_renderer.sourceColorRamp().count(), 5)
# write to xml and read
doc = QDomDocument('testdoc')
@ -350,6 +359,8 @@ class TestQgsRasterLayer(unittest.TestCase):
self.assertEqual(classes[3].label, 'new class')
self.assertEqual(classes[1].color.name(), '#00ff00')
self.assertEqual(classes[3].color.name(), '#ff0000')
self.assertEqual(restored.sourceColorRamp().type(), 'random')
self.assertEqual(restored.sourceColorRamp().count(), 5)
# render test
layer.setRenderer(renderer)
@ -376,5 +387,6 @@ class TestQgsRasterLayer(unittest.TestCase):
self.assertEqual(classes[3].color.name(), '#00ff00')
self.assertEqual(classes[6].color.name(), '#0000ff')
if __name__ == '__main__':
unittest.main()