QGIS API Documentation  3.20.0-Odense (decaadbb31)
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"
22 #include "qgsrastertransparency.h"
23 #include "qgsrasterviewport.h"
24 #include "qgsstyleentityvisitor.h"
25 #include "qgscolorramplegendnode.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 
104 {
105  mShader.reset( shader );
106 }
107 
108 void QgsSingleBandPseudoColorRenderer::createShader( QgsColorRamp *colorRamp, QgsColorRampShader::Type colorRampType, QgsColorRampShader::ClassificationMode classificationMode, int classes, bool clip, const QgsRectangle &extent )
109 {
110  if ( band() == -1 || classificationMin() >= classificationMax() )
111  {
112  return;
113  }
114 
115  QgsColorRampShader *colorRampShader = new QgsColorRampShader( classificationMin(), classificationMax(), colorRamp, colorRampType, classificationMode );
116  colorRampShader->classifyColorRamp( classes, band(), extent, input() );
117  colorRampShader->setClip( clip );
118 
119  QgsRasterShader *rasterShader = new QgsRasterShader();
120  rasterShader->setRasterShaderFunction( colorRampShader );
121  setShader( rasterShader );
122 }
123 
125 {
126  if ( elem.isNull() )
127  {
128  return nullptr;
129  }
130 
131  int band = elem.attribute( QStringLiteral( "band" ), QStringLiteral( "-1" ) ).toInt();
132  QgsRasterShader *shader = nullptr;
133  QDomElement rasterShaderElem = elem.firstChildElement( QStringLiteral( "rastershader" ) );
134  if ( !rasterShaderElem.isNull() )
135  {
136  shader = new QgsRasterShader();
137  shader->readXml( rasterShaderElem );
138  }
139 
141  r->readXml( elem );
142 
143  // TODO: add _readXML in superclass?
144  r->setClassificationMin( elem.attribute( QStringLiteral( "classificationMin" ), QStringLiteral( "NaN" ) ).toDouble() );
145  r->setClassificationMax( elem.attribute( QStringLiteral( "classificationMax" ), QStringLiteral( "NaN" ) ).toDouble() );
146 
147  // Backward compatibility with serialization of QGIS 2.X era
148  QString minMaxOrigin = elem.attribute( QStringLiteral( "classificationMinMaxOrigin" ) );
149  if ( !minMaxOrigin.isEmpty() )
150  {
151  if ( minMaxOrigin.contains( QLatin1String( "MinMax" ) ) )
152  {
154  }
155  else if ( minMaxOrigin.contains( QLatin1String( "CumulativeCut" ) ) )
156  {
158  }
159  else if ( minMaxOrigin.contains( QLatin1String( "StdDev" ) ) )
160  {
162  }
163  else
164  {
166  }
167 
168  if ( minMaxOrigin.contains( QLatin1String( "FullExtent" ) ) )
169  {
171  }
172  else if ( minMaxOrigin.contains( QLatin1String( "SubExtent" ) ) )
173  {
175  }
176  else
177  {
179  }
180 
181  if ( minMaxOrigin.contains( QLatin1String( "Estimated" ) ) )
182  {
184  }
185  else // if ( minMaxOrigin.contains( QLatin1String( "Exact" ) ) )
186  {
188  }
189  }
190 
191  return r;
192 }
193 
194 QgsRasterBlock *QgsSingleBandPseudoColorRenderer::block( int bandNo, QgsRectangle const &extent, int width, int height, QgsRasterBlockFeedback *feedback )
195 {
196  Q_UNUSED( bandNo )
197 
198  std::unique_ptr< QgsRasterBlock > outputBlock( new QgsRasterBlock() );
199  if ( !mInput || !mShader || !mShader->rasterShaderFunction() )
200  {
201  return outputBlock.release();
202  }
203 
204 
205  std::shared_ptr< QgsRasterBlock > inputBlock( mInput->block( mBand, extent, width, height, feedback ) );
206  if ( !inputBlock || inputBlock->isEmpty() )
207  {
208  QgsDebugMsg( QStringLiteral( "No raster data!" ) );
209  return outputBlock.release();
210  }
211 
212  //rendering is faster without considering user-defined transparency
213  bool hasTransparency = usesTransparency();
214 
215  std::shared_ptr< QgsRasterBlock > alphaBlock;
216  if ( mAlphaBand > 0 && mAlphaBand != mBand )
217  {
218  alphaBlock.reset( mInput->block( mAlphaBand, extent, width, height, feedback ) );
219  if ( !alphaBlock || alphaBlock->isEmpty() )
220  {
221  return outputBlock.release();
222  }
223  }
224  else if ( mAlphaBand == mBand )
225  {
226  alphaBlock = inputBlock;
227  }
228 
229  if ( !outputBlock->reset( Qgis::DataType::ARGB32_Premultiplied, width, height ) )
230  {
231  return outputBlock.release();
232  }
233 
234  const QRgb myDefaultColor = renderColorForNodataPixel();
235  QRgb *outputBlockData = outputBlock->colorData();
236  const QgsRasterShaderFunction *fcn = mShader->rasterShaderFunction();
237 
238  qgssize count = ( qgssize )width * height;
239  bool isNoData = false;
240  for ( qgssize i = 0; i < count; i++ )
241  {
242  double val = inputBlock->valueAndNoData( i, isNoData );
243  if ( isNoData )
244  {
245  outputBlockData[i] = myDefaultColor;
246  continue;
247  }
248 
249  int red, green, blue, alpha;
250  if ( !fcn->shade( val, &red, &green, &blue, &alpha ) )
251  {
252  outputBlockData[i] = myDefaultColor;
253  continue;
254  }
255 
256  if ( alpha < 255 )
257  {
258  // Working with premultiplied colors, so multiply values by alpha
259  red *= ( alpha / 255.0 );
260  blue *= ( alpha / 255.0 );
261  green *= ( alpha / 255.0 );
262  }
263 
264  if ( !hasTransparency )
265  {
266  outputBlockData[i] = qRgba( red, green, blue, alpha );
267  }
268  else
269  {
270  //opacity
271  double currentOpacity = mOpacity;
272  if ( mRasterTransparency )
273  {
274  currentOpacity = mRasterTransparency->alphaValue( val, mOpacity * 255 ) / 255.0;
275  }
276  if ( mAlphaBand > 0 )
277  {
278  currentOpacity *= alphaBlock->value( i ) / 255.0;
279  }
280 
281  outputBlockData[i] = qRgba( currentOpacity * red, currentOpacity * green, currentOpacity * blue, currentOpacity * alpha );
282  }
283  }
284 
285  return outputBlock.release();
286 }
287 
288 void QgsSingleBandPseudoColorRenderer::writeXml( QDomDocument &doc, QDomElement &parentElem ) const
289 {
290  if ( parentElem.isNull() )
291  {
292  return;
293  }
294 
295  QDomElement rasterRendererElem = doc.createElement( QStringLiteral( "rasterrenderer" ) );
296  _writeXml( doc, rasterRendererElem );
297  rasterRendererElem.setAttribute( QStringLiteral( "band" ), mBand );
298  if ( mShader )
299  {
300  mShader->writeXml( doc, rasterRendererElem ); //todo: include color ramp items directly in this renderer
301  }
302  rasterRendererElem.setAttribute( QStringLiteral( "classificationMin" ), QgsRasterBlock::printValue( mClassificationMin ) );
303  rasterRendererElem.setAttribute( QStringLiteral( "classificationMax" ), QgsRasterBlock::printValue( mClassificationMax ) );
304 
305  parentElem.appendChild( rasterRendererElem );
306 }
307 
308 QList< QPair< QString, QColor > > QgsSingleBandPseudoColorRenderer::legendSymbologyItems() const
309 {
310  QList< QPair< QString, QColor > > symbolItems;
311  if ( mShader )
312  {
313  QgsRasterShaderFunction *shaderFunction = mShader->rasterShaderFunction();
314  if ( shaderFunction )
315  {
316  shaderFunction->legendSymbologyItems( symbolItems );
317  }
318  }
319  return symbolItems;
320 }
321 
323 {
324  QList<int> bandList;
325  if ( mBand != -1 )
326  {
327  bandList << mBand;
328  }
329  return bandList;
330 }
331 
332 void QgsSingleBandPseudoColorRenderer::toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
333 {
334  // create base structure
335  QgsRasterRenderer::toSld( doc, element, props );
336 
337  // look for RasterSymbolizer tag
338  QDomNodeList elements = element.elementsByTagName( QStringLiteral( "sld:RasterSymbolizer" ) );
339  if ( elements.size() == 0 )
340  return;
341 
342  // there SHOULD be only one
343  QDomElement rasterSymbolizerElem = elements.at( 0 ).toElement();
344 
345  // add Channel Selection tags
346  QDomElement channelSelectionElem = doc.createElement( QStringLiteral( "sld:ChannelSelection" ) );
347  rasterSymbolizerElem.appendChild( channelSelectionElem );
348 
349  // for the mapped band
350  QDomElement channelElem = doc.createElement( QStringLiteral( "sld:GrayChannel" ) );
351  channelSelectionElem.appendChild( channelElem );
352 
353  // set band
354  QDomElement sourceChannelNameElem = doc.createElement( QStringLiteral( "sld:SourceChannelName" ) );
355  sourceChannelNameElem.appendChild( doc.createTextNode( QString::number( band() ) ) );
356  channelElem.appendChild( sourceChannelNameElem );
357 
358  // add ColorMap tag
359  QDomElement colorMapElem = doc.createElement( QStringLiteral( "sld:ColorMap" ) );
360 
361  // set type of ColorMap ramp [ramp, intervals, values]
362  // basing on interpolation algorithm of the raster shader
363  QString rampType = QStringLiteral( "ramp" );
364  const QgsColorRampShader *rampShader = dynamic_cast<const QgsColorRampShader *>( mShader->rasterShaderFunction() );
365  if ( !rampShader )
366  return;
367 
368  switch ( rampShader->colorRampType() )
369  {
370  case ( QgsColorRampShader::Exact ):
371  rampType = QStringLiteral( "values" );
372  break;
374  rampType = QStringLiteral( "intervals" );
375  break;
377  rampType = QStringLiteral( "ramp" );
378  break;
379  }
380 
381  colorMapElem.setAttribute( QStringLiteral( "type" ), rampType );
382  if ( rampShader->colorRampItemList().size() >= 255 )
383  colorMapElem.setAttribute( QStringLiteral( "extended" ), QStringLiteral( "true" ) );
384  rasterSymbolizerElem.appendChild( colorMapElem );
385 
386  // for each color set a ColorMapEntry tag nested into "sld:ColorMap" tag
387  // e.g. <ColorMapEntry color="#EEBE2F" quantity="-300" label="label" opacity="0"/>
388  QList<QgsColorRampShader::ColorRampItem> classes = rampShader->colorRampItemList();
389  QList<QgsColorRampShader::ColorRampItem>::const_iterator classDataIt = classes.constBegin();
390  for ( ; classDataIt != classes.constEnd(); ++classDataIt )
391  {
392  QDomElement colorMapEntryElem = doc.createElement( QStringLiteral( "sld:ColorMapEntry" ) );
393  colorMapElem.appendChild( colorMapEntryElem );
394 
395  // set colorMapEntryElem attributes
396  colorMapEntryElem.setAttribute( QStringLiteral( "color" ), classDataIt->color.name() );
397  colorMapEntryElem.setAttribute( QStringLiteral( "quantity" ), classDataIt->value );
398  colorMapEntryElem.setAttribute( QStringLiteral( "label" ), classDataIt->label );
399  if ( classDataIt->color.alphaF() != 1.0 )
400  {
401  colorMapEntryElem.setAttribute( QStringLiteral( "opacity" ), QString::number( classDataIt->color.alphaF() ) );
402  }
403  }
404 }
405 
407 {
408  if ( const QgsColorRampShader *shader = dynamic_cast< const QgsColorRampShader * >( mShader->rasterShaderFunction() ) )
409  {
410  QgsStyleColorRampEntity entity( shader->sourceColorRamp() );
411  if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity ) ) )
412  return false;
413  }
414 
415  return true;
416 }
417 
418 QList<QgsLayerTreeModelLegendNode *> QgsSingleBandPseudoColorRenderer::createLegendNodes( QgsLayerTreeLayer *nodeLayer )
419 {
420  if ( !mShader )
421  return QList<QgsLayerTreeModelLegendNode *>();
422 
423  const QgsColorRampShader *rampShader = dynamic_cast<const QgsColorRampShader *>( mShader->rasterShaderFunction() );
424  if ( !rampShader )
425  return QList<QgsLayerTreeModelLegendNode *>();
426 
427  QList<QgsLayerTreeModelLegendNode *> res;
428 
429  const QString name = displayBandName( mBand );
430  if ( !name.isEmpty() )
431  {
432  res << new QgsSimpleLegendNode( nodeLayer, name );
433  }
434 
435  switch ( rampShader->colorRampType() )
436  {
438  // for interpolated shaders we use a ramp legend node unless the settings flag
439  // to use the continuous legend is not set, in that case we fall through
440  if ( ! rampShader->legendSettings() || rampShader->legendSettings()->useContinuousLegend() )
441  {
442  if ( !rampShader->colorRampItemList().isEmpty() )
443  {
444  res << new QgsColorRampLegendNode( nodeLayer, rampShader->createColorRamp(),
445  rampShader->legendSettings() ? *rampShader->legendSettings() : QgsColorRampLegendNodeSettings(),
446  rampShader->minimumValue(), rampShader->maximumValue() );
447  }
448  break;
449  }
450  Q_FALLTHROUGH();
453  {
454  // for all others we use itemised lists
455  const QList< QPair< QString, QColor > > items = legendSymbologyItems();
456  res.reserve( items.size() );
457  for ( const QPair< QString, QColor > &item : items )
458  {
459  res << new QgsRasterSymbolLegendNode( nodeLayer, item.second, item.first );
460  }
461  break;
462  }
463  }
464  return res;
465 }
@ 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.
QList< QgsColorRampShader::ColorRampItem > colorRampItemList() const
Returns the custom colormap.
const QgsColorRampLegendNodeSettings * legendSettings() const
Returns the color ramp shader legend settings.
Type colorRampType() const
Returns the color ramp type.
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:32
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.
virtual QgsRasterInterface * input() const
Current input.
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.
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.
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.
const QgsRasterMinMaxOrigin & minMaxOrigin() const
Returns const reference to origin of min/max values.
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.
QgsRasterShader * shader()
Returns the raster shader.
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.
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.
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:1251
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:1051
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
Contains information relating to the style entity currently being visited.