QGIS API Documentation 3.28.0-Firenze (ed3ad0430f)
qgssinglebandpseudocolorrenderer.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgssinglebandpseudocolorrenderer.cpp
3 ------------------------------------
4 begin : January 2012
5 copyright : (C) 2012 by Marco Hugentobler
6 email : marco at sourcepole dot ch
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
19#include "qgscolorramp.h"
20#include "qgscolorrampshader.h"
21#include "qgsrastershader.h"
23#include "qgsrasterviewport.h"
26
27#include <QDomDocument>
28#include <QDomElement>
29#include <QImage>
30
32 : QgsRasterRenderer( input, QStringLiteral( "singlebandpseudocolor" ) )
33 , mShader( shader )
34 , mBand( band )
35 , mClassificationMin( std::numeric_limits<double>::quiet_NaN() )
36 , mClassificationMax( std::numeric_limits<double>::quiet_NaN() )
37{
38}
39
41{
42 if ( !mInput )
43 {
44 mBand = bandNo;
45 return;
46 }
47
48 if ( bandNo <= mInput->bandCount() || bandNo > 0 )
49 {
50 mBand = bandNo;
51 }
52}
53
55{
56 mClassificationMin = min;
57 if ( auto *lShader = shader() )
58 {
59 QgsColorRampShader *colorRampShader = dynamic_cast<QgsColorRampShader *>( lShader->rasterShaderFunction() );
60 if ( colorRampShader )
61 {
62 colorRampShader->setMinimumValue( min );
63 }
64 }
65}
66
68{
69 mClassificationMax = max;
70 if ( auto *lShader = shader() )
71 {
72 QgsColorRampShader *colorRampShader = dynamic_cast<QgsColorRampShader *>( lShader->rasterShaderFunction() );
73 if ( colorRampShader )
74 {
75 colorRampShader->setMaximumValue( max );
76 }
77 }
78}
79
81{
82 QgsRasterShader *shader = nullptr;
83
84 if ( mShader )
85 {
86 shader = new QgsRasterShader( mShader->minimumValue(), mShader->maximumValue() );
87
88 // Shader function
89 const QgsColorRampShader *origColorRampShader = dynamic_cast<const QgsColorRampShader *>( mShader->rasterShaderFunction() );
90
91 if ( origColorRampShader )
92 {
93 QgsColorRampShader *colorRampShader = new QgsColorRampShader( *origColorRampShader );
94 shader->setRasterShaderFunction( colorRampShader );
95 }
96 }
98 renderer->copyCommonProperties( this );
99
100 return renderer;
101}
102
103Qgis::RasterRendererFlags QgsSingleBandPseudoColorRenderer::flags() const
104{
106}
107
109{
110 mShader.reset( shader );
111}
112
113void QgsSingleBandPseudoColorRenderer::createShader( QgsColorRamp *colorRamp, QgsColorRampShader::Type colorRampType, QgsColorRampShader::ClassificationMode classificationMode, int classes, bool clip, const QgsRectangle &extent )
114{
115 if ( band() == -1 || classificationMin() >= classificationMax() )
116 {
117 return;
118 }
119
120 QgsColorRampShader *colorRampShader = new QgsColorRampShader( classificationMin(), classificationMax(), colorRamp, colorRampType, classificationMode );
121 colorRampShader->classifyColorRamp( classes, band(), extent, input() );
122 colorRampShader->setClip( clip );
123
124 QgsRasterShader *rasterShader = new QgsRasterShader();
125 rasterShader->setRasterShaderFunction( colorRampShader );
126 setShader( rasterShader );
127}
128
130{
131 if ( elem.isNull() )
132 {
133 return nullptr;
134 }
135
136 const int band = elem.attribute( QStringLiteral( "band" ), QStringLiteral( "-1" ) ).toInt();
137 QgsRasterShader *shader = nullptr;
138 const QDomElement rasterShaderElem = elem.firstChildElement( QStringLiteral( "rastershader" ) );
139 if ( !rasterShaderElem.isNull() )
140 {
141 shader = new QgsRasterShader();
142 shader->readXml( rasterShaderElem );
143 }
144
146 r->readXml( elem );
147
148 // TODO: add _readXML in superclass?
149 r->setClassificationMin( elem.attribute( QStringLiteral( "classificationMin" ), QStringLiteral( "NaN" ) ).toDouble() );
150 r->setClassificationMax( elem.attribute( QStringLiteral( "classificationMax" ), QStringLiteral( "NaN" ) ).toDouble() );
151
152 // Backward compatibility with serialization of QGIS 2.X era
153 const QString minMaxOrigin = elem.attribute( QStringLiteral( "classificationMinMaxOrigin" ) );
154 if ( !minMaxOrigin.isEmpty() )
155 {
156 if ( minMaxOrigin.contains( QLatin1String( "MinMax" ) ) )
157 {
159 }
160 else if ( minMaxOrigin.contains( QLatin1String( "CumulativeCut" ) ) )
161 {
163 }
164 else if ( minMaxOrigin.contains( QLatin1String( "StdDev" ) ) )
165 {
167 }
168 else
169 {
171 }
172
173 if ( minMaxOrigin.contains( QLatin1String( "FullExtent" ) ) )
174 {
176 }
177 else if ( minMaxOrigin.contains( QLatin1String( "SubExtent" ) ) )
178 {
180 }
181 else
182 {
184 }
185
186 if ( minMaxOrigin.contains( QLatin1String( "Estimated" ) ) )
187 {
189 }
190 else // if ( minMaxOrigin.contains( QLatin1String( "Exact" ) ) )
191 {
193 }
194 }
195
196 return r;
197}
198
199QgsRasterBlock *QgsSingleBandPseudoColorRenderer::block( int bandNo, QgsRectangle const &extent, int width, int height, QgsRasterBlockFeedback *feedback )
200{
201 Q_UNUSED( bandNo )
202
203 std::unique_ptr< QgsRasterBlock > outputBlock( new QgsRasterBlock() );
204 if ( !mInput || !mShader || !mShader->rasterShaderFunction() )
205 {
206 return outputBlock.release();
207 }
208
209
210 const std::shared_ptr< QgsRasterBlock > inputBlock( mInput->block( mBand, extent, width, height, feedback ) );
211 if ( !inputBlock || inputBlock->isEmpty() )
212 {
213 QgsDebugMsg( QStringLiteral( "No raster data!" ) );
214 return outputBlock.release();
215 }
216
217 //rendering is faster without considering user-defined transparency
218 const bool hasTransparency = usesTransparency();
219
220 std::shared_ptr< QgsRasterBlock > alphaBlock;
221 if ( mAlphaBand > 0 && mAlphaBand != mBand )
222 {
223 alphaBlock.reset( mInput->block( mAlphaBand, extent, width, height, feedback ) );
224 if ( !alphaBlock || alphaBlock->isEmpty() )
225 {
226 return outputBlock.release();
227 }
228 }
229 else if ( mAlphaBand == mBand )
230 {
231 alphaBlock = inputBlock;
232 }
233
234 if ( !outputBlock->reset( Qgis::DataType::ARGB32_Premultiplied, width, height ) )
235 {
236 return outputBlock.release();
237 }
238
239 const QRgb myDefaultColor = renderColorForNodataPixel();
240 QRgb *outputBlockData = outputBlock->colorData();
241 const QgsRasterShaderFunction *fcn = mShader->rasterShaderFunction();
242
243 const qgssize count = ( qgssize )width * height;
244 bool isNoData = false;
245 for ( qgssize i = 0; i < count; i++ )
246 {
247 const double val = inputBlock->valueAndNoData( i, isNoData );
248 if ( isNoData )
249 {
250 outputBlockData[i] = myDefaultColor;
251 continue;
252 }
253
254 int red, green, blue, alpha;
255 if ( !fcn->shade( val, &red, &green, &blue, &alpha ) )
256 {
257 outputBlockData[i] = myDefaultColor;
258 continue;
259 }
260
261 if ( alpha < 255 )
262 {
263 // Working with premultiplied colors, so multiply values by alpha
264 red *= ( alpha / 255.0 );
265 blue *= ( alpha / 255.0 );
266 green *= ( alpha / 255.0 );
267 }
268
269 if ( !hasTransparency )
270 {
271 outputBlockData[i] = qRgba( red, green, blue, alpha );
272 }
273 else
274 {
275 //opacity
276 double currentOpacity = mOpacity;
278 {
279 currentOpacity = mRasterTransparency->alphaValue( val, mOpacity * 255 ) / 255.0;
280 }
281 if ( mAlphaBand > 0 )
282 {
283 currentOpacity *= alphaBlock->value( i ) / 255.0;
284 }
285
286 outputBlockData[i] = qRgba( currentOpacity * red, currentOpacity * green, currentOpacity * blue, currentOpacity * alpha );
287 }
288 }
289
290 return outputBlock.release();
291}
292
293void QgsSingleBandPseudoColorRenderer::writeXml( QDomDocument &doc, QDomElement &parentElem ) const
294{
295 if ( parentElem.isNull() )
296 {
297 return;
298 }
299
300 QDomElement rasterRendererElem = doc.createElement( QStringLiteral( "rasterrenderer" ) );
301 _writeXml( doc, rasterRendererElem );
302 rasterRendererElem.setAttribute( QStringLiteral( "band" ), mBand );
303 if ( mShader )
304 {
305 mShader->writeXml( doc, rasterRendererElem ); //todo: include color ramp items directly in this renderer
306 }
307 rasterRendererElem.setAttribute( QStringLiteral( "classificationMin" ), QgsRasterBlock::printValue( mClassificationMin ) );
308 rasterRendererElem.setAttribute( QStringLiteral( "classificationMax" ), QgsRasterBlock::printValue( mClassificationMax ) );
309
310 parentElem.appendChild( rasterRendererElem );
311}
312
313QList< QPair< QString, QColor > > QgsSingleBandPseudoColorRenderer::legendSymbologyItems() const
314{
315 QList< QPair< QString, QColor > > symbolItems;
316 if ( mShader )
317 {
318 QgsRasterShaderFunction *shaderFunction = mShader->rasterShaderFunction();
319 if ( shaderFunction )
320 {
321 shaderFunction->legendSymbologyItems( symbolItems );
322 }
323 }
324 return symbolItems;
325}
326
328{
329 QList<int> bandList;
330 if ( mBand != -1 )
331 {
332 bandList << mBand;
333 }
334 return bandList;
335}
336
337void QgsSingleBandPseudoColorRenderer::toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
338{
339 // create base structure
340 QgsRasterRenderer::toSld( doc, element, props );
341
342 // look for RasterSymbolizer tag
343 const QDomNodeList elements = element.elementsByTagName( QStringLiteral( "sld:RasterSymbolizer" ) );
344 if ( elements.size() == 0 )
345 return;
346
347 // there SHOULD be only one
348 QDomElement rasterSymbolizerElem = elements.at( 0 ).toElement();
349
350 // add Channel Selection tags
351 QDomElement channelSelectionElem = doc.createElement( QStringLiteral( "sld:ChannelSelection" ) );
352 rasterSymbolizerElem.appendChild( channelSelectionElem );
353
354 // for the mapped band
355 QDomElement channelElem = doc.createElement( QStringLiteral( "sld:GrayChannel" ) );
356 channelSelectionElem.appendChild( channelElem );
357
358 // set band
359 QDomElement sourceChannelNameElem = doc.createElement( QStringLiteral( "sld:SourceChannelName" ) );
360 sourceChannelNameElem.appendChild( doc.createTextNode( QString::number( band() ) ) );
361 channelElem.appendChild( sourceChannelNameElem );
362
363 // add ColorMap tag
364 QDomElement colorMapElem = doc.createElement( QStringLiteral( "sld:ColorMap" ) );
365
366 // set type of ColorMap ramp [ramp, intervals, values]
367 // basing on interpolation algorithm of the raster shader
368 QString rampType = QStringLiteral( "ramp" );
369 const QgsColorRampShader *rampShader = dynamic_cast<const QgsColorRampShader *>( mShader->rasterShaderFunction() );
370 if ( !rampShader )
371 return;
372
373 switch ( rampShader->colorRampType() )
374 {
376 rampType = QStringLiteral( "values" );
377 break;
379 rampType = QStringLiteral( "intervals" );
380 break;
382 rampType = QStringLiteral( "ramp" );
383 break;
384 }
385
386 colorMapElem.setAttribute( QStringLiteral( "type" ), rampType );
387 if ( rampShader->colorRampItemList().size() >= 255 )
388 colorMapElem.setAttribute( QStringLiteral( "extended" ), QStringLiteral( "true" ) );
389 rasterSymbolizerElem.appendChild( colorMapElem );
390
391 // for each color set a ColorMapEntry tag nested into "sld:ColorMap" tag
392 // e.g. <ColorMapEntry color="#EEBE2F" quantity="-300" label="label" opacity="0"/>
393 const QList<QgsColorRampShader::ColorRampItem> classes = rampShader->colorRampItemList();
394 QList<QgsColorRampShader::ColorRampItem>::const_iterator classDataIt = classes.constBegin();
395 for ( ; classDataIt != classes.constEnd(); ++classDataIt )
396 {
397 QDomElement colorMapEntryElem = doc.createElement( QStringLiteral( "sld:ColorMapEntry" ) );
398 colorMapElem.appendChild( colorMapEntryElem );
399
400 // set colorMapEntryElem attributes
401 colorMapEntryElem.setAttribute( QStringLiteral( "color" ), classDataIt->color.name() );
402 colorMapEntryElem.setAttribute( QStringLiteral( "quantity" ), classDataIt->value );
403 colorMapEntryElem.setAttribute( QStringLiteral( "label" ), classDataIt->label );
404 if ( classDataIt->color.alphaF() != 1.0 )
405 {
406 colorMapEntryElem.setAttribute( QStringLiteral( "opacity" ), QString::number( classDataIt->color.alphaF() ) );
407 }
408 }
409}
410
412{
413 if ( const QgsColorRampShader *shader = dynamic_cast< const QgsColorRampShader * >( mShader->rasterShaderFunction() ) )
414 {
415 QgsStyleColorRampEntity entity( shader->sourceColorRamp() );
416 if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity ) ) )
417 return false;
418 }
419
420 return true;
421}
422
423QList<QgsLayerTreeModelLegendNode *> QgsSingleBandPseudoColorRenderer::createLegendNodes( QgsLayerTreeLayer *nodeLayer )
424{
425 if ( !mShader )
426 return QList<QgsLayerTreeModelLegendNode *>();
427
428 const QgsColorRampShader *rampShader = dynamic_cast<const QgsColorRampShader *>( mShader->rasterShaderFunction() );
429 if ( !rampShader )
430 return QList<QgsLayerTreeModelLegendNode *>();
431
432 QList<QgsLayerTreeModelLegendNode *> res;
433
434 const QString name = displayBandName( mBand );
435 if ( !name.isEmpty() )
436 {
437 res << new QgsSimpleLegendNode( nodeLayer, name );
438 }
439
440 switch ( rampShader->colorRampType() )
441 {
443 // for interpolated shaders we use a ramp legend node unless the settings flag
444 // to use the continuous legend is not set, in that case we fall through
445 if ( ! rampShader->legendSettings() || rampShader->legendSettings()->useContinuousLegend() )
446 {
447 if ( !rampShader->colorRampItemList().isEmpty() )
448 {
449 res << new QgsColorRampLegendNode( nodeLayer, rampShader->createColorRamp(),
450 rampShader->legendSettings() ? *rampShader->legendSettings() : QgsColorRampLegendNodeSettings(),
451 rampShader->minimumValue(), rampShader->maximumValue() );
452 }
453 break;
454 }
455 Q_FALLTHROUGH();
458 {
459 // for all others we use itemised lists
460 const QList< QPair< QString, QColor > > items = legendSymbologyItems();
461 res.reserve( items.size() );
462 for ( const QPair< QString, QColor > &item : items )
463 {
464 res << new QgsRasterSymbolLegendNode( nodeLayer, item.second, item.first );
465 }
466 break;
467 }
468 }
469 return res;
470}
@ InternalLayerOpacityHandling
The renderer internally handles the raster layer's opacity, so the default layer level opacity handli...
@ ARGB32_Premultiplied
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32_Premultiplied.
Settings for a color ramp legend node.
bool useContinuousLegend() const
Returns true if a continuous gradient legend will be used.
A legend node which renders a color ramp.
A ramp shader will color a raster pixel based on a list of values ranges in a ramp.
const QgsColorRampLegendNodeSettings * legendSettings() const
Returns the color ramp shader legend settings.
Type colorRampType() const
Returns the color ramp type.
QList< QgsColorRampShader::ColorRampItem > colorRampItemList() const
Returns the custom colormap.
ClassificationMode
Classification modes used to create the color ramp shader.
void setClip(bool clip)
Sets whether the shader should not render values out of range.
QgsColorRamp * createColorRamp() const
Creates a gradient color ramp from shader settings.
Type
Supported methods for color interpolation.
@ Interpolated
Interpolates the color between two class breaks linearly.
@ Discrete
Assigns the color of the higher class for every pixel between two class breaks.
@ Exact
Assigns the color of the exact matching value in the color ramp item list.
void classifyColorRamp(int classes=0, int band=-1, const QgsRectangle &extent=QgsRectangle(), QgsRasterInterface *input=nullptr)
Classify color ramp shader.
Abstract base class for color ramps.
Definition: qgscolorramp.h:30
Layer tree node points to a map layer.
Feedback object tailored for raster block reading.
Raster data container.
static QString printValue(double value)
Print double value with all necessary significant digits.
Base class for processing filters like renderers, reprojector, resampler etc.
virtual QgsRasterBlock * block(int bandNo, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback *feedback=nullptr)=0
Read block of data using given extent and size.
QString displayBandName(int bandNumber) const
Generates a friendly, descriptive name for the specified bandNumber.
QgsRasterInterface * mInput
virtual QgsRectangle extent() const
Gets the extent of the interface.
virtual QgsRasterInterface * input() const
Current input.
void setExtent(QgsRasterMinMaxOrigin::Extent extent)
Sets the extent.
@ Exact
Exact statistics.
@ Estimated
Approximated statistics.
void setLimits(QgsRasterMinMaxOrigin::Limits limits)
Sets the limits.
void setStatAccuracy(QgsRasterMinMaxOrigin::StatAccuracy accuracy)
Sets the statistics accuracy.
@ CurrentCanvas
Current extent of the canvas (at the time of computation) is used to compute statistics.
@ WholeRaster
Whole raster is used to compute statistics.
@ StdDev
Range is [ mean - stdDevFactor() * stddev, mean + stdDevFactor() * stddev ].
@ MinMax
Real min-max values.
@ CumulativeCut
Range is [ min + cumulativeCutLower() * (max - min), min + cumulativeCutUpper() * (max - min) ].
Raster renderer pipe that applies colors to a raster.
double mOpacity
Global alpha value (0-1)
int mAlphaBand
Read alpha value from band.
QRgb renderColorForNodataPixel() const
Returns the color for the renderer to use to represent nodata pixels.
const QgsRasterMinMaxOrigin & minMaxOrigin() const
Returns const reference to origin of min/max values.
void _writeXml(QDomDocument &doc, QDomElement &rasterRendererElem) const
Write upper class info into rasterrenderer element (called by writeXml method of subclasses)
int bandCount() const override
Gets number of bands.
QgsRasterMinMaxOrigin mMinMaxOrigin
Origin of min/max values.
QgsRasterTransparency * mRasterTransparency
Raster transparency per color or value. Overwrites global alpha value.
bool usesTransparency() const
void copyCommonProperties(const QgsRasterRenderer *other, bool copyMinMaxOrigin=true)
Copies common properties like opacity / transparency data from other renderer.
virtual void toSld(QDomDocument &doc, QDomElement &element, const QVariantMap &props=QVariantMap()) const
Used from subclasses to create SLD Rule elements following SLD v1.0 specs.
void readXml(const QDomElement &rendererElem) override
Sets base class members from xml. Usually called from create() methods of subclasses.
The raster shade function applies a shader to a pixel at render time - typically used to render grays...
virtual void legendSymbologyItems(QList< QPair< QString, QColor > > &symbolItems) const
Returns legend symbology items if provided by renderer.
double maximumValue() const
Returns the minimum value for the raster shader.
virtual bool shade(double value, int *returnRedValue, int *returnGreenValue, int *returnBlueValue, int *returnAlpha) const
Generates an new RGBA value based on one input value.
virtual void setMaximumValue(double value)
Sets the maximum value for the raster shader.
virtual void setMinimumValue(double value)
Sets the minimum value for the raster shader.
double minimumValue() const
Returns the maximum value for the raster shader.
Interface for all raster shaders.
void readXml(const QDomElement &elem, const QgsReadWriteContext &context=QgsReadWriteContext())
Reads shader state from an XML element.
void setRasterShaderFunction(QgsRasterShaderFunction *function)
A public method that allows the user to set their own shader function.
Implementation of legend node interface for displaying raster legend entries.
int alphaValue(double value, int globalTransparency=255) const
Returns the transparency value for a single value pixel.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
Implementation of legend node interface for displaying arbitrary label with icon.
Raster renderer pipe for single band pseudocolor.
QgsSingleBandPseudoColorRenderer * clone() const override
Clone itself, create deep copy.
void createShader(QgsColorRamp *colorRamp=nullptr, QgsColorRampShader::Type colorRampType=QgsColorRampShader::Interpolated, QgsColorRampShader::ClassificationMode classificationMode=QgsColorRampShader::Continuous, int classes=0, bool clip=false, const QgsRectangle &extent=QgsRectangle())
Creates a color ramp shader.
QList< QPair< QString, QColor > > legendSymbologyItems() const override
Returns symbology items if provided by renderer.
void writeXml(QDomDocument &doc, QDomElement &parentElem) const override
Write base class members to xml.
int band() const
Returns the band used by the renderer.
void setBand(int bandNo)
Sets the band used by the renderer.
void setShader(QgsRasterShader *shader)
Takes ownership of the shader.
QList< QgsLayerTreeModelLegendNode * > createLegendNodes(QgsLayerTreeLayer *nodeLayer) override
Creates a set of legend nodes representing the renderer.
QgsRasterShader * shader()
Returns the raster shader.
QList< int > usesBands() const override
Returns a list of band numbers used by the renderer.
QgsRasterBlock * block(int bandNo, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback *feedback=nullptr) override
Read block of data using given extent and size.
Qgis::RasterRendererFlags flags() const override
Returns flags which dictate renderer behavior.
QgsSingleBandPseudoColorRenderer(QgsRasterInterface *input, int band=-1, QgsRasterShader *shader=nullptr)
Note: takes ownership of QgsRasterShader.
static QgsRasterRenderer * create(const QDomElement &elem, QgsRasterInterface *input)
bool accept(QgsStyleEntityVisitorInterface *visitor) const override
Accepts the specified symbology visitor, causing it to visit all symbols associated with the renderer...
void toSld(QDomDocument &doc, QDomElement &element, const QVariantMap &props=QVariantMap()) const override
Used from subclasses to create SLD Rule elements following SLD v1.0 specs.
A color ramp entity for QgsStyle databases.
Definition: qgsstyle.h:1374
An interface for classes which can visit style entity (e.g.
virtual bool visit(const QgsStyleEntityVisitorInterface::StyleLeaf &entity)
Called when the visitor will visit a style entity.
unsigned long long qgssize
Qgssize is used instead of size_t, because size_t is stdlib type, unknown by SIP, and it would be har...
Definition: qgis.h:3032
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
Contains information relating to the style entity currently being visited.