QGIS API Documentation  3.8.0-Zanzibar (11aff65)
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 
25 #include <QDomDocument>
26 #include <QDomElement>
27 #include <QImage>
28 
30  : QgsRasterRenderer( input, QStringLiteral( "singlebandpseudocolor" ) )
31  , mShader( shader )
32  , mBand( band )
33  , mClassificationMin( std::numeric_limits<double>::quiet_NaN() )
34  , mClassificationMax( std::numeric_limits<double>::quiet_NaN() )
35 {
36 }
37 
39 {
40  if ( bandNo > mInput->bandCount() || bandNo <= 0 )
41  {
42  return;
43  }
44  mBand = bandNo;
45 }
46 
48 {
49  mClassificationMin = min;
50  if ( shader() )
51  {
52  QgsColorRampShader *colorRampShader = dynamic_cast<QgsColorRampShader *>( shader()->rasterShaderFunction() );
53  if ( colorRampShader )
54  {
55  colorRampShader->setMinimumValue( min );
56  }
57  }
58 }
59 
61 {
62  mClassificationMax = max;
63  if ( shader() )
64  {
65  QgsColorRampShader *colorRampShader = dynamic_cast<QgsColorRampShader *>( shader()->rasterShaderFunction() );
66  if ( colorRampShader )
67  {
68  colorRampShader->setMaximumValue( max );
69  }
70  }
71 }
72 
74 {
75  QgsRasterShader *shader = nullptr;
76 
77  if ( mShader )
78  {
79  shader = new QgsRasterShader( mShader->minimumValue(), mShader->maximumValue() );
80 
81  // Shader function
82  const QgsColorRampShader *origColorRampShader = dynamic_cast<const QgsColorRampShader *>( mShader->rasterShaderFunction() );
83 
84  if ( origColorRampShader )
85  {
86  QgsColorRampShader *colorRampShader = new QgsColorRampShader( mShader->minimumValue(), mShader->maximumValue() );
87 
88  if ( origColorRampShader->sourceColorRamp() )
89  {
90  colorRampShader->setSourceColorRamp( origColorRampShader->sourceColorRamp()->clone() );
91  }
92  colorRampShader->setColorRampType( origColorRampShader->colorRampType() );
93  colorRampShader->setClassificationMode( origColorRampShader->classificationMode() );
94  colorRampShader->setClip( origColorRampShader->clip() );
95  colorRampShader->setColorRampItemList( origColorRampShader->colorRampItemList() );
96  shader->setRasterShaderFunction( colorRampShader );
97  }
98  }
99  QgsSingleBandPseudoColorRenderer *renderer = new QgsSingleBandPseudoColorRenderer( nullptr, mBand, shader );
100  renderer->copyCommonProperties( this );
101 
102  return renderer;
103 }
104 
106 {
107  mShader.reset( shader );
108 }
109 
111 {
112  if ( band() == -1 || classificationMin() >= classificationMax() )
113  {
114  return;
115  }
116 
117  QgsColorRampShader *colorRampShader = new QgsColorRampShader( classificationMin(), classificationMax(), colorRamp, colorRampType, classificationMode );
118  colorRampShader->classifyColorRamp( classes, band(), extent, input() );
119  colorRampShader->setClip( clip );
120 
121  QgsRasterShader *rasterShader = new QgsRasterShader();
122  rasterShader->setRasterShaderFunction( colorRampShader );
123  setShader( rasterShader );
124 }
125 
127 {
128  if ( elem.isNull() )
129  {
130  return nullptr;
131  }
132 
133  int band = elem.attribute( QStringLiteral( "band" ), QStringLiteral( "-1" ) ).toInt();
134  QgsRasterShader *shader = nullptr;
135  QDomElement rasterShaderElem = elem.firstChildElement( QStringLiteral( "rastershader" ) );
136  if ( !rasterShaderElem.isNull() )
137  {
138  shader = new QgsRasterShader();
139  shader->readXml( rasterShaderElem );
140  }
141 
143  r->readXml( elem );
144 
145  // TODO: add _readXML in superclass?
146  r->setClassificationMin( elem.attribute( QStringLiteral( "classificationMin" ), QStringLiteral( "NaN" ) ).toDouble() );
147  r->setClassificationMax( elem.attribute( QStringLiteral( "classificationMax" ), QStringLiteral( "NaN" ) ).toDouble() );
148 
149  // Backward compatibility with serialization of QGIS 2.X era
150  QString minMaxOrigin = elem.attribute( QStringLiteral( "classificationMinMaxOrigin" ) );
151  if ( !minMaxOrigin.isEmpty() )
152  {
153  if ( minMaxOrigin.contains( QLatin1String( "MinMax" ) ) )
154  {
156  }
157  else if ( minMaxOrigin.contains( QLatin1String( "CumulativeCut" ) ) )
158  {
160  }
161  else if ( minMaxOrigin.contains( QLatin1String( "StdDev" ) ) )
162  {
164  }
165  else
166  {
168  }
169 
170  if ( minMaxOrigin.contains( QLatin1String( "FullExtent" ) ) )
171  {
173  }
174  else if ( minMaxOrigin.contains( QLatin1String( "SubExtent" ) ) )
175  {
177  }
178  else
179  {
181  }
182 
183  if ( minMaxOrigin.contains( QLatin1String( "Estimated" ) ) )
184  {
186  }
187  else // if ( minMaxOrigin.contains( QLatin1String( "Exact" ) ) )
188  {
190  }
191  }
192 
193  return r;
194 }
195 
197 {
198  Q_UNUSED( bandNo )
199 
200  std::unique_ptr< QgsRasterBlock > outputBlock( new QgsRasterBlock() );
201  if ( !mInput || !mShader || !mShader->rasterShaderFunction() )
202  {
203  return outputBlock.release();
204  }
205 
206 
207  std::shared_ptr< QgsRasterBlock > inputBlock( mInput->block( mBand, extent, width, height, feedback ) );
208  if ( !inputBlock || inputBlock->isEmpty() )
209  {
210  QgsDebugMsg( QStringLiteral( "No raster data!" ) );
211  return outputBlock.release();
212  }
213 
214  //rendering is faster without considering user-defined transparency
215  bool hasTransparency = usesTransparency();
216 
217  std::shared_ptr< QgsRasterBlock > alphaBlock;
218  if ( mAlphaBand > 0 && mAlphaBand != mBand )
219  {
220  alphaBlock.reset( mInput->block( mAlphaBand, extent, width, height, feedback ) );
221  if ( !alphaBlock || alphaBlock->isEmpty() )
222  {
223  return outputBlock.release();
224  }
225  }
226  else if ( mAlphaBand == mBand )
227  {
228  alphaBlock = inputBlock;
229  }
230 
231  if ( !outputBlock->reset( Qgis::ARGB32_Premultiplied, width, height ) )
232  {
233  return outputBlock.release();
234  }
235 
236  QRgb myDefaultColor = NODATA_COLOR;
237  QRgb *outputBlockData = outputBlock->colorData();
238  const QgsRasterShaderFunction *fcn = mShader->rasterShaderFunction();
239 
240  qgssize count = ( qgssize )width * height;
241  bool isNoData = false;
242  for ( qgssize i = 0; i < count; i++ )
243  {
244  double val = inputBlock->valueAndNoData( i, isNoData );
245  if ( isNoData )
246  {
247  outputBlockData[i] = myDefaultColor;
248  continue;
249  }
250 
251  int red, green, blue, alpha;
252  if ( !fcn->shade( val, &red, &green, &blue, &alpha ) )
253  {
254  outputBlockData[i] = myDefaultColor;
255  continue;
256  }
257 
258  if ( alpha < 255 )
259  {
260  // Working with premultiplied colors, so multiply values by alpha
261  red *= ( alpha / 255.0 );
262  blue *= ( alpha / 255.0 );
263  green *= ( alpha / 255.0 );
264  }
265 
266  if ( !hasTransparency )
267  {
268  outputBlockData[i] = qRgba( red, green, blue, alpha );
269  }
270  else
271  {
272  //opacity
273  double currentOpacity = mOpacity;
274  if ( mRasterTransparency )
275  {
276  currentOpacity = mRasterTransparency->alphaValue( val, mOpacity * 255 ) / 255.0;
277  }
278  if ( mAlphaBand > 0 )
279  {
280  currentOpacity *= alphaBlock->value( i ) / 255.0;
281  }
282 
283  outputBlockData[i] = qRgba( currentOpacity * red, currentOpacity * green, currentOpacity * blue, currentOpacity * alpha );
284  }
285  }
286 
287  return outputBlock.release();
288 }
289 
290 void QgsSingleBandPseudoColorRenderer::writeXml( QDomDocument &doc, QDomElement &parentElem ) const
291 {
292  if ( parentElem.isNull() )
293  {
294  return;
295  }
296 
297  QDomElement rasterRendererElem = doc.createElement( QStringLiteral( "rasterrenderer" ) );
298  _writeXml( doc, rasterRendererElem );
299  rasterRendererElem.setAttribute( QStringLiteral( "band" ), mBand );
300  if ( mShader )
301  {
302  mShader->writeXml( doc, rasterRendererElem ); //todo: include color ramp items directly in this renderer
303  }
304  rasterRendererElem.setAttribute( QStringLiteral( "classificationMin" ), QgsRasterBlock::printValue( mClassificationMin ) );
305  rasterRendererElem.setAttribute( QStringLiteral( "classificationMax" ), QgsRasterBlock::printValue( mClassificationMax ) );
306 
307  parentElem.appendChild( rasterRendererElem );
308 }
309 
310 void QgsSingleBandPseudoColorRenderer::legendSymbologyItems( QList< QPair< QString, QColor > > &symbolItems ) const
311 {
312  if ( mShader )
313  {
314  QgsRasterShaderFunction *shaderFunction = mShader->rasterShaderFunction();
315  if ( shaderFunction )
316  {
317  shaderFunction->legendSymbologyItems( symbolItems );
318  }
319  }
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 QgsStringMap &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  {
367  switch ( rampShader->colorRampType() )
368  {
369  case ( QgsColorRampShader::Exact ):
370  rampType = QStringLiteral( "values" );
371  break;
373  rampType = QStringLiteral( "intervals" );
374  break;
376  rampType = QStringLiteral( "ramp" );
377  break;
378  }
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 }
virtual int bandCount() const =0
Gets number of bands.
virtual void setMinimumValue(double value)
Sets the minimum value for the raster shader.
A rectangle specified with double values.
Definition: qgsrectangle.h:41
Interface for all raster shaders.
void setColorRampItemList(const QList< QgsColorRampShader::ColorRampItem > &list)
Sets a custom colormap.
static QString printValue(double value)
Print double value with all necessary significant digits.
void legendSymbologyItems(QList< QPair< QString, QColor > > &symbolItems) const override
Gets symbology items if provided by renderer.
virtual void toSld(QDomDocument &doc, QDomElement &element, const QgsStringMap &props=QgsStringMap()) const
Used from subclasses to create SLD Rule elements following SLD v1.0 specs.
virtual bool shade(double value, int *returnRedValue, int *returnGreenValue, int *returnBlueValue, int *returnAlpha) const
Generates an new RGBA value based on one input value.
QgsSingleBandPseudoColorRenderer * clone() const override
Clone itself, create deep copy.
virtual QgsRectangle extent() const
Gets the extent of the interface.
static QgsRasterRenderer * create(const QDomElement &elem, QgsRasterInterface *input)
A ramp shader will color a raster pixel based on a list of values ranges in a ramp.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
Abstract base class for color ramps.
Definition: qgscolorramp.h:31
virtual QgsRasterInterface * input() const
Current input.
QList< QgsColorRampShader::ColorRampItem > colorRampItemList() const
Returns the custom colormap.
QgsRasterMinMaxOrigin mMinMaxOrigin
Origin of min/max values.
const QgsRasterMinMaxOrigin & minMaxOrigin() const
Returns const reference to origin of min/max values.
void setClip(bool clip)
Sets whether the shader should not render values out of range.
virtual void legendSymbologyItems(QList< QPair< QString, QColor > > &symbolItems) const
Returns legend symbology items if provided by renderer.
QgsRasterShader * shader()
Returns the raster shader.
QMap< QString, QString > QgsStringMap
Definition: qgis.h:587
void setShader(QgsRasterShader *shader)
Takes ownership of the shader.
Type
Supported methods for color interpolation.
Current extent of the canvas (at the time of computation) is used to compute statistics.
void setExtent(QgsRasterMinMaxOrigin::Extent extent)
Sets the extent.
virtual void setMaximumValue(double value)
Sets the maximum value for the raster shader.
QgsRasterTransparency * mRasterTransparency
Raster transparency per color or value. Overwrites global alpha value.
static const QRgb NODATA_COLOR
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32_Premultiplied.
Definition: qgis.h:94
void setColorRampType(QgsColorRampShader::Type colorRampType)
Sets the color ramp type.
Raster data container.
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.
QgsRasterShaderFunction * rasterShaderFunction()
void _writeXml(QDomDocument &doc, QDomElement &rasterRendererElem) const
Write upper class info into rasterrenderer element (called by writeXml method of subclasses) ...
The raster shade function applies a shader to a pixel at render time - typically used to render grays...
void copyCommonProperties(const QgsRasterRenderer *other, bool copyMinMaxOrigin=true)
Copies common properties like opacity / transparency data from other renderer.
Raster renderer pipe for single band pseudocolor.
void setRasterShaderFunction(QgsRasterShaderFunction *function)
A public method that allows the user to set their own shader function.
Range is [ mean - stdDevFactor() * stddev, mean + stdDevFactor() * stddev ].
bool usesTransparency() const
int mAlphaBand
Read alpha value from band.
void readXml(const QDomElement &rendererElem) override
Sets base class members from xml. Usually called from create() methods of subclasses.
void setSourceColorRamp(QgsColorRamp *colorramp)
Set the source color ramp.
QList< int > usesBands() const override
Returns a list of band numbers used by the renderer.
Range is [ min + cumulativeCutLower() * (max - min), min + cumulativeCutUpper() * (max - min) ]...
Base class for processing filters like renderers, reprojector, resampler etc.
int alphaValue(double value, int globalTransparency=255) const
Returns the transparency value for a single value pixel.
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:596
void classifyColorRamp(int classes=0, int band=-1, const QgsRectangle &extent=QgsRectangle(), QgsRasterInterface *input=nullptr)
Classify color ramp shader.
void setLimits(QgsRasterMinMaxOrigin::Limits limits)
Sets the limits.
Type colorRampType() const
Returns the color ramp type.
Assigns the color of the exact matching value in the color ramp item list.
void setStatAccuracy(QgsRasterMinMaxOrigin::StatAccuracy accuracy)
Sets the statistics accuracy.
Whole raster is used to compute statistics.
void setClassificationMode(ClassificationMode classificationMode)
Sets classification mode.
void setBand(int bandNo)
Sets the band used by the renderer.
double mOpacity
Global alpha value (0-1)
Interpolates the color between two class breaks linearly.
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.
ClassificationMode
Classification modes used to create the color ramp shader.
Assigns the color of the higher class for every pixel between two class breaks.
QgsRasterInterface * mInput
QgsSingleBandPseudoColorRenderer(QgsRasterInterface *input, int band=-1, QgsRasterShader *shader=nullptr)
Note: takes ownership of QgsRasterShader.
void toSld(QDomDocument &doc, QDomElement &element, const QgsStringMap &props=QgsStringMap()) const override
Used from subclasses to create SLD Rule elements following SLD v1.0 specs.
Feedback object tailored for raster block reading.
QgsRasterBlock * block(int bandNo, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback *feedback=nullptr) override
Read block of data using given extent and size.
Raster renderer pipe that applies colors to a raster.
int band() const
Returns the band used by the renderer.
void writeXml(QDomDocument &doc, QDomElement &parentElem) const override
Write base class members to xml.
void readXml(const QDomElement &elem)
Reads shader state from an XML element.