QGIS API Documentation  3.6.0-Noosa (5873452)
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  QgsStringMap newProps = props;
335 
336  // create base structure
337  QgsRasterRenderer::toSld( doc, element, props );
338 
339  // look for RasterSymbolizer tag
340  QDomNodeList elements = element.elementsByTagName( QStringLiteral( "sld:RasterSymbolizer" ) );
341  if ( elements.size() == 0 )
342  return;
343 
344  // there SHOULD be only one
345  QDomElement rasterSymbolizerElem = elements.at( 0 ).toElement();
346 
347  // add Channel Selection tags
348  QDomElement channelSelectionElem = doc.createElement( QStringLiteral( "sld:ChannelSelection" ) );
349  rasterSymbolizerElem.appendChild( channelSelectionElem );
350 
351  // for the mapped band
352  QDomElement channelElem = doc.createElement( QStringLiteral( "sld:GrayChannel" ) );
353  channelSelectionElem.appendChild( channelElem );
354 
355  // set band
356  QDomElement sourceChannelNameElem = doc.createElement( QStringLiteral( "sld:SourceChannelName" ) );
357  sourceChannelNameElem.appendChild( doc.createTextNode( QString::number( band() ) ) );
358  channelElem.appendChild( sourceChannelNameElem );
359 
360  // add ColorMap tag
361  QDomElement colorMapElem = doc.createElement( QStringLiteral( "sld:ColorMap" ) );
362 
363  // set type of ColorMap ramp [ramp, intervals, values]
364  // basing on interpolation algorithm of the raster shader
365  QString rampType = QStringLiteral( "ramp" );
366  const QgsColorRampShader *rampShader = dynamic_cast<const QgsColorRampShader *>( mShader->rasterShaderFunction() );
367  if ( rampShader )
368  {
369  switch ( rampShader->colorRampType() )
370  {
371  case ( QgsColorRampShader::Exact ):
372  rampType = QStringLiteral( "values" );
373  break;
375  rampType = QStringLiteral( "intervals" );
376  break;
378  rampType = QStringLiteral( "ramp" );
379  break;
380  }
381  }
382 
383  colorMapElem.setAttribute( QStringLiteral( "type" ), rampType );
384  if ( rampShader->colorRampItemList().size() >= 255 )
385  colorMapElem.setAttribute( QStringLiteral( "extended" ), QStringLiteral( "true" ) );
386  rasterSymbolizerElem.appendChild( colorMapElem );
387 
388  // for each color set a ColorMapEntry tag nested into "sld:ColorMap" tag
389  // e.g. <ColorMapEntry color="#EEBE2F" quantity="-300" label="label" opacity="0"/>
390  QList<QgsColorRampShader::ColorRampItem> classes = rampShader->colorRampItemList();
391  QList<QgsColorRampShader::ColorRampItem>::const_iterator classDataIt = classes.constBegin();
392  for ( ; classDataIt != classes.constEnd(); ++classDataIt )
393  {
394  QDomElement colorMapEntryElem = doc.createElement( QStringLiteral( "sld:ColorMapEntry" ) );
395  colorMapElem.appendChild( colorMapEntryElem );
396 
397  // set colorMapEntryElem attributes
398  colorMapEntryElem.setAttribute( QStringLiteral( "color" ), classDataIt->color.name() );
399  colorMapEntryElem.setAttribute( QStringLiteral( "quantity" ), classDataIt->value );
400  colorMapEntryElem.setAttribute( QStringLiteral( "label" ), classDataIt->label );
401  if ( classDataIt->color.alphaF() != 1.0 )
402  {
403  colorMapEntryElem.setAttribute( QStringLiteral( "opacity" ), QString::number( classDataIt->color.alphaF() ) );
404  }
405  }
406 }
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.