QGIS API Documentation  3.13.0-Master (b73bd58cfb)
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 
26 #include <QDomDocument>
27 #include <QDomElement>
28 #include <QImage>
29 
31  : QgsRasterRenderer( input, QStringLiteral( "singlebandpseudocolor" ) )
32  , mShader( shader )
33  , mBand( band )
34  , mClassificationMin( std::numeric_limits<double>::quiet_NaN() )
35  , mClassificationMax( std::numeric_limits<double>::quiet_NaN() )
36 {
37 }
38 
40 {
41  if ( !mInput )
42  {
43  mBand = bandNo;
44  return;
45  }
46 
47  if ( bandNo <= mInput->bandCount() || bandNo > 0 )
48  {
49  mBand = bandNo;
50  }
51 }
52 
54 {
55  mClassificationMin = min;
56  if ( shader() )
57  {
58  QgsColorRampShader *colorRampShader = dynamic_cast<QgsColorRampShader *>( shader()->rasterShaderFunction() );
59  if ( colorRampShader )
60  {
61  colorRampShader->setMinimumValue( min );
62  }
63  }
64 }
65 
67 {
68  mClassificationMax = max;
69  if ( shader() )
70  {
71  QgsColorRampShader *colorRampShader = dynamic_cast<QgsColorRampShader *>( shader()->rasterShaderFunction() );
72  if ( colorRampShader )
73  {
74  colorRampShader->setMaximumValue( max );
75  }
76  }
77 }
78 
80 {
81  QgsRasterShader *shader = nullptr;
82 
83  if ( mShader )
84  {
85  shader = new QgsRasterShader( mShader->minimumValue(), mShader->maximumValue() );
86 
87  // Shader function
88  const QgsColorRampShader *origColorRampShader = dynamic_cast<const QgsColorRampShader *>( mShader->rasterShaderFunction() );
89 
90  if ( origColorRampShader )
91  {
92  QgsColorRampShader *colorRampShader = new QgsColorRampShader( mShader->minimumValue(), mShader->maximumValue() );
93 
94  if ( origColorRampShader->sourceColorRamp() )
95  {
96  colorRampShader->setSourceColorRamp( origColorRampShader->sourceColorRamp()->clone() );
97  }
98  colorRampShader->setColorRampType( origColorRampShader->colorRampType() );
99  colorRampShader->setClassificationMode( origColorRampShader->classificationMode() );
100  colorRampShader->setClip( origColorRampShader->clip() );
101  colorRampShader->setColorRampItemList( origColorRampShader->colorRampItemList() );
102  shader->setRasterShaderFunction( colorRampShader );
103  }
104  }
105  QgsSingleBandPseudoColorRenderer *renderer = new QgsSingleBandPseudoColorRenderer( nullptr, mBand, shader );
106  renderer->copyCommonProperties( this );
107 
108  return renderer;
109 }
110 
112 {
113  mShader.reset( shader );
114 }
115 
117 {
118  if ( band() == -1 || classificationMin() >= classificationMax() )
119  {
120  return;
121  }
122 
123  QgsColorRampShader *colorRampShader = new QgsColorRampShader( classificationMin(), classificationMax(), colorRamp, colorRampType, classificationMode );
124  colorRampShader->classifyColorRamp( classes, band(), extent, input() );
125  colorRampShader->setClip( clip );
126 
127  QgsRasterShader *rasterShader = new QgsRasterShader();
128  rasterShader->setRasterShaderFunction( colorRampShader );
129  setShader( rasterShader );
130 }
131 
133 {
134  if ( elem.isNull() )
135  {
136  return nullptr;
137  }
138 
139  int band = elem.attribute( QStringLiteral( "band" ), QStringLiteral( "-1" ) ).toInt();
140  QgsRasterShader *shader = nullptr;
141  QDomElement rasterShaderElem = elem.firstChildElement( QStringLiteral( "rastershader" ) );
142  if ( !rasterShaderElem.isNull() )
143  {
144  shader = new QgsRasterShader();
145  shader->readXml( rasterShaderElem );
146  }
147 
149  r->readXml( elem );
150 
151  // TODO: add _readXML in superclass?
152  r->setClassificationMin( elem.attribute( QStringLiteral( "classificationMin" ), QStringLiteral( "NaN" ) ).toDouble() );
153  r->setClassificationMax( elem.attribute( QStringLiteral( "classificationMax" ), QStringLiteral( "NaN" ) ).toDouble() );
154 
155  // Backward compatibility with serialization of QGIS 2.X era
156  QString minMaxOrigin = elem.attribute( QStringLiteral( "classificationMinMaxOrigin" ) );
157  if ( !minMaxOrigin.isEmpty() )
158  {
159  if ( minMaxOrigin.contains( QLatin1String( "MinMax" ) ) )
160  {
162  }
163  else if ( minMaxOrigin.contains( QLatin1String( "CumulativeCut" ) ) )
164  {
166  }
167  else if ( minMaxOrigin.contains( QLatin1String( "StdDev" ) ) )
168  {
170  }
171  else
172  {
174  }
175 
176  if ( minMaxOrigin.contains( QLatin1String( "FullExtent" ) ) )
177  {
179  }
180  else if ( minMaxOrigin.contains( QLatin1String( "SubExtent" ) ) )
181  {
183  }
184  else
185  {
187  }
188 
189  if ( minMaxOrigin.contains( QLatin1String( "Estimated" ) ) )
190  {
192  }
193  else // if ( minMaxOrigin.contains( QLatin1String( "Exact" ) ) )
194  {
196  }
197  }
198 
199  return r;
200 }
201 
203 {
204  Q_UNUSED( bandNo )
205 
206  std::unique_ptr< QgsRasterBlock > outputBlock( new QgsRasterBlock() );
207  if ( !mInput || !mShader || !mShader->rasterShaderFunction() )
208  {
209  return outputBlock.release();
210  }
211 
212 
213  std::shared_ptr< QgsRasterBlock > inputBlock( mInput->block( mBand, extent, width, height, feedback ) );
214  if ( !inputBlock || inputBlock->isEmpty() )
215  {
216  QgsDebugMsg( QStringLiteral( "No raster data!" ) );
217  return outputBlock.release();
218  }
219 
220  //rendering is faster without considering user-defined transparency
221  bool hasTransparency = usesTransparency();
222 
223  std::shared_ptr< QgsRasterBlock > alphaBlock;
224  if ( mAlphaBand > 0 && mAlphaBand != mBand )
225  {
226  alphaBlock.reset( mInput->block( mAlphaBand, extent, width, height, feedback ) );
227  if ( !alphaBlock || alphaBlock->isEmpty() )
228  {
229  return outputBlock.release();
230  }
231  }
232  else if ( mAlphaBand == mBand )
233  {
234  alphaBlock = inputBlock;
235  }
236 
237  if ( !outputBlock->reset( Qgis::ARGB32_Premultiplied, width, height ) )
238  {
239  return outputBlock.release();
240  }
241 
242  const QRgb myDefaultColor = renderColorForNodataPixel();
243  QRgb *outputBlockData = outputBlock->colorData();
244  const QgsRasterShaderFunction *fcn = mShader->rasterShaderFunction();
245 
246  qgssize count = ( qgssize )width * height;
247  bool isNoData = false;
248  for ( qgssize i = 0; i < count; i++ )
249  {
250  double val = inputBlock->valueAndNoData( i, isNoData );
251  if ( isNoData )
252  {
253  outputBlockData[i] = myDefaultColor;
254  continue;
255  }
256 
257  int red, green, blue, alpha;
258  if ( !fcn->shade( val, &red, &green, &blue, &alpha ) )
259  {
260  outputBlockData[i] = myDefaultColor;
261  continue;
262  }
263 
264  if ( alpha < 255 )
265  {
266  // Working with premultiplied colors, so multiply values by alpha
267  red *= ( alpha / 255.0 );
268  blue *= ( alpha / 255.0 );
269  green *= ( alpha / 255.0 );
270  }
271 
272  if ( !hasTransparency )
273  {
274  outputBlockData[i] = qRgba( red, green, blue, alpha );
275  }
276  else
277  {
278  //opacity
279  double currentOpacity = mOpacity;
280  if ( mRasterTransparency )
281  {
282  currentOpacity = mRasterTransparency->alphaValue( val, mOpacity * 255 ) / 255.0;
283  }
284  if ( mAlphaBand > 0 )
285  {
286  currentOpacity *= alphaBlock->value( i ) / 255.0;
287  }
288 
289  outputBlockData[i] = qRgba( currentOpacity * red, currentOpacity * green, currentOpacity * blue, currentOpacity * alpha );
290  }
291  }
292 
293  return outputBlock.release();
294 }
295 
296 void QgsSingleBandPseudoColorRenderer::writeXml( QDomDocument &doc, QDomElement &parentElem ) const
297 {
298  if ( parentElem.isNull() )
299  {
300  return;
301  }
302 
303  QDomElement rasterRendererElem = doc.createElement( QStringLiteral( "rasterrenderer" ) );
304  _writeXml( doc, rasterRendererElem );
305  rasterRendererElem.setAttribute( QStringLiteral( "band" ), mBand );
306  if ( mShader )
307  {
308  mShader->writeXml( doc, rasterRendererElem ); //todo: include color ramp items directly in this renderer
309  }
310  rasterRendererElem.setAttribute( QStringLiteral( "classificationMin" ), QgsRasterBlock::printValue( mClassificationMin ) );
311  rasterRendererElem.setAttribute( QStringLiteral( "classificationMax" ), QgsRasterBlock::printValue( mClassificationMax ) );
312 
313  parentElem.appendChild( rasterRendererElem );
314 }
315 
316 void QgsSingleBandPseudoColorRenderer::legendSymbologyItems( QList< QPair< QString, QColor > > &symbolItems ) const
317 {
318  if ( mShader )
319  {
320  QgsRasterShaderFunction *shaderFunction = mShader->rasterShaderFunction();
321  if ( shaderFunction )
322  {
323  shaderFunction->legendSymbologyItems( symbolItems );
324  }
325  }
326 }
327 
329 {
330  QList<int> bandList;
331  if ( mBand != -1 )
332  {
333  bandList << mBand;
334  }
335  return bandList;
336 }
337 
338 void QgsSingleBandPseudoColorRenderer::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
339 {
340  // create base structure
341  QgsRasterRenderer::toSld( doc, element, props );
342 
343  // look for RasterSymbolizer tag
344  QDomNodeList elements = element.elementsByTagName( QStringLiteral( "sld:RasterSymbolizer" ) );
345  if ( elements.size() == 0 )
346  return;
347 
348  // there SHOULD be only one
349  QDomElement rasterSymbolizerElem = elements.at( 0 ).toElement();
350 
351  // add Channel Selection tags
352  QDomElement channelSelectionElem = doc.createElement( QStringLiteral( "sld:ChannelSelection" ) );
353  rasterSymbolizerElem.appendChild( channelSelectionElem );
354 
355  // for the mapped band
356  QDomElement channelElem = doc.createElement( QStringLiteral( "sld:GrayChannel" ) );
357  channelSelectionElem.appendChild( channelElem );
358 
359  // set band
360  QDomElement sourceChannelNameElem = doc.createElement( QStringLiteral( "sld:SourceChannelName" ) );
361  sourceChannelNameElem.appendChild( doc.createTextNode( QString::number( band() ) ) );
362  channelElem.appendChild( sourceChannelNameElem );
363 
364  // add ColorMap tag
365  QDomElement colorMapElem = doc.createElement( QStringLiteral( "sld:ColorMap" ) );
366 
367  // set type of ColorMap ramp [ramp, intervals, values]
368  // basing on interpolation algorithm of the raster shader
369  QString rampType = QStringLiteral( "ramp" );
370  const QgsColorRampShader *rampShader = dynamic_cast<const QgsColorRampShader *>( mShader->rasterShaderFunction() );
371  if ( !rampShader )
372  return;
373 
374  switch ( rampShader->colorRampType() )
375  {
376  case ( QgsColorRampShader::Exact ):
377  rampType = QStringLiteral( "values" );
378  break;
380  rampType = QStringLiteral( "intervals" );
381  break;
383  rampType = QStringLiteral( "ramp" );
384  break;
385  }
386 
387  colorMapElem.setAttribute( QStringLiteral( "type" ), rampType );
388  if ( rampShader->colorRampItemList().size() >= 255 )
389  colorMapElem.setAttribute( QStringLiteral( "extended" ), QStringLiteral( "true" ) );
390  rasterSymbolizerElem.appendChild( colorMapElem );
391 
392  // for each color set a ColorMapEntry tag nested into "sld:ColorMap" tag
393  // e.g. <ColorMapEntry color="#EEBE2F" quantity="-300" label="label" opacity="0"/>
394  QList<QgsColorRampShader::ColorRampItem> classes = rampShader->colorRampItemList();
395  QList<QgsColorRampShader::ColorRampItem>::const_iterator classDataIt = classes.constBegin();
396  for ( ; classDataIt != classes.constEnd(); ++classDataIt )
397  {
398  QDomElement colorMapEntryElem = doc.createElement( QStringLiteral( "sld:ColorMapEntry" ) );
399  colorMapElem.appendChild( colorMapEntryElem );
400 
401  // set colorMapEntryElem attributes
402  colorMapEntryElem.setAttribute( QStringLiteral( "color" ), classDataIt->color.name() );
403  colorMapEntryElem.setAttribute( QStringLiteral( "quantity" ), classDataIt->value );
404  colorMapEntryElem.setAttribute( QStringLiteral( "label" ), classDataIt->label );
405  if ( classDataIt->color.alphaF() != 1.0 )
406  {
407  colorMapEntryElem.setAttribute( QStringLiteral( "opacity" ), QString::number( classDataIt->color.alphaF() ) );
408  }
409  }
410 }
411 
413 {
414  if ( const QgsColorRampShader *shader = dynamic_cast< const QgsColorRampShader * >( mShader->rasterShaderFunction() ) )
415  {
416  QgsStyleColorRampEntity entity( shader->sourceColorRamp() );
417  if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity ) ) )
418  return false;
419  }
420 
421  return true;
422 }
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
bool accept(QgsStyleEntityVisitorInterface *visitor) const override
Accepts the specified symbology visitor, causing it to visit all symbols associated with the renderer...
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.
An interface for classes which can visit style entity (e.g.
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:694
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.
virtual bool visit(const QgsStyleEntityVisitorInterface::StyleLeaf &entity)
Called when the visitor will visit a style entity.
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.
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32_Premultiplied.
Definition: qgis.h:116
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.
int bandCount() const override
Gets number of bands.
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:703
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.
QRgb renderColorForNodataPixel() const
Returns the color for the renderer to use to represent nodata pixels.
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.
A color ramp entity for QgsStyle databases.
Definition: qgsstyle.h:1003
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.
Contains information relating to the style entity currently being visited.
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.