QGIS API Documentation  3.4.15-Madeira (e83d02e274)
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 ( !mInput )
41  {
42  mBand = bandNo;
43  return;
44  }
45 
46  if ( bandNo <= mInput->bandCount() || bandNo > 0 )
47  {
48  mBand = bandNo;
49  }
50 }
51 
53 {
54  mClassificationMin = min;
55  if ( shader() )
56  {
57  QgsColorRampShader *colorRampShader = dynamic_cast<QgsColorRampShader *>( shader()->rasterShaderFunction() );
58  if ( colorRampShader )
59  {
60  colorRampShader->setMinimumValue( min );
61  }
62  }
63 }
64 
66 {
67  mClassificationMax = max;
68  if ( shader() )
69  {
70  QgsColorRampShader *colorRampShader = dynamic_cast<QgsColorRampShader *>( shader()->rasterShaderFunction() );
71  if ( colorRampShader )
72  {
73  colorRampShader->setMaximumValue( max );
74  }
75  }
76 }
77 
79 {
80  QgsRasterShader *shader = nullptr;
81 
82  if ( mShader )
83  {
84  shader = new QgsRasterShader( mShader->minimumValue(), mShader->maximumValue() );
85 
86  // Shader function
87  const QgsColorRampShader *origColorRampShader = dynamic_cast<const QgsColorRampShader *>( mShader->rasterShaderFunction() );
88 
89  if ( origColorRampShader )
90  {
91  QgsColorRampShader *colorRampShader = new QgsColorRampShader( mShader->minimumValue(), mShader->maximumValue() );
92 
93  if ( origColorRampShader->sourceColorRamp() )
94  {
95  colorRampShader->setSourceColorRamp( origColorRampShader->sourceColorRamp()->clone() );
96  }
97  colorRampShader->setColorRampType( origColorRampShader->colorRampType() );
98  colorRampShader->setClassificationMode( origColorRampShader->classificationMode() );
99  colorRampShader->setClip( origColorRampShader->clip() );
100  colorRampShader->setColorRampItemList( origColorRampShader->colorRampItemList() );
101  shader->setRasterShaderFunction( colorRampShader );
102  }
103  }
104  QgsSingleBandPseudoColorRenderer *renderer = new QgsSingleBandPseudoColorRenderer( nullptr, mBand, shader );
105  renderer->copyCommonProperties( this );
106 
107  return renderer;
108 }
109 
111 {
112  mShader.reset( shader );
113 }
114 
116 {
117  if ( band() == -1 || classificationMin() >= classificationMax() )
118  {
119  return;
120  }
121 
122  QgsColorRampShader *colorRampShader = new QgsColorRampShader( classificationMin(), classificationMax(), colorRamp, colorRampType, classificationMode );
123  colorRampShader->classifyColorRamp( classes, band(), extent, input() );
124  colorRampShader->setClip( clip );
125 
126  QgsRasterShader *rasterShader = new QgsRasterShader();
127  rasterShader->setRasterShaderFunction( colorRampShader );
128  setShader( rasterShader );
129 }
130 
132 {
133  if ( elem.isNull() )
134  {
135  return nullptr;
136  }
137 
138  int band = elem.attribute( QStringLiteral( "band" ), QStringLiteral( "-1" ) ).toInt();
139  QgsRasterShader *shader = nullptr;
140  QDomElement rasterShaderElem = elem.firstChildElement( QStringLiteral( "rastershader" ) );
141  if ( !rasterShaderElem.isNull() )
142  {
143  shader = new QgsRasterShader();
144  shader->readXml( rasterShaderElem );
145  }
146 
148  r->readXml( elem );
149 
150  // TODO: add _readXML in superclass?
151  r->setClassificationMin( elem.attribute( QStringLiteral( "classificationMin" ), QStringLiteral( "NaN" ) ).toDouble() );
152  r->setClassificationMax( elem.attribute( QStringLiteral( "classificationMax" ), QStringLiteral( "NaN" ) ).toDouble() );
153 
154  // Backward compatibility with serialization of QGIS 2.X era
155  QString minMaxOrigin = elem.attribute( QStringLiteral( "classificationMinMaxOrigin" ) );
156  if ( !minMaxOrigin.isEmpty() )
157  {
158  if ( minMaxOrigin.contains( QLatin1String( "MinMax" ) ) )
159  {
161  }
162  else if ( minMaxOrigin.contains( QLatin1String( "CumulativeCut" ) ) )
163  {
165  }
166  else if ( minMaxOrigin.contains( QLatin1String( "StdDev" ) ) )
167  {
169  }
170  else
171  {
173  }
174 
175  if ( minMaxOrigin.contains( QLatin1String( "FullExtent" ) ) )
176  {
178  }
179  else if ( minMaxOrigin.contains( QLatin1String( "SubExtent" ) ) )
180  {
182  }
183  else
184  {
186  }
187 
188  if ( minMaxOrigin.contains( QLatin1String( "Estimated" ) ) )
189  {
191  }
192  else // if ( minMaxOrigin.contains( QLatin1String( "Exact" ) ) )
193  {
195  }
196  }
197 
198  return r;
199 }
200 
202 {
203  Q_UNUSED( bandNo );
204 
205  std::unique_ptr< QgsRasterBlock > outputBlock( new QgsRasterBlock() );
206  if ( !mInput || !mShader || !mShader->rasterShaderFunction() )
207  {
208  return outputBlock.release();
209  }
210 
211 
212  std::shared_ptr< QgsRasterBlock > inputBlock( mInput->block( mBand, extent, width, height, feedback ) );
213  if ( !inputBlock || inputBlock->isEmpty() )
214  {
215  QgsDebugMsg( QStringLiteral( "No raster data!" ) );
216  return outputBlock.release();
217  }
218 
219  //rendering is faster without considering user-defined transparency
220  bool hasTransparency = usesTransparency();
221 
222  std::shared_ptr< QgsRasterBlock > alphaBlock;
223  if ( mAlphaBand > 0 && mAlphaBand != mBand )
224  {
225  alphaBlock.reset( mInput->block( mAlphaBand, extent, width, height, feedback ) );
226  if ( !alphaBlock || alphaBlock->isEmpty() )
227  {
228  return outputBlock.release();
229  }
230  }
231  else if ( mAlphaBand == mBand )
232  {
233  alphaBlock = inputBlock;
234  }
235 
236  if ( !outputBlock->reset( Qgis::ARGB32_Premultiplied, width, height ) )
237  {
238  return outputBlock.release();
239  }
240 
241  QRgb myDefaultColor = NODATA_COLOR;
242  QRgb *outputBlockData = outputBlock->colorData();
243  const QgsRasterShaderFunction *fcn = mShader->rasterShaderFunction();
244 
245  qgssize count = ( qgssize )width * height;
246  for ( qgssize i = 0; i < count; i++ )
247  {
248  if ( inputBlock->isNoData( i ) )
249  {
250  outputBlockData[i] = myDefaultColor;
251  continue;
252  }
253  double val = inputBlock->value( i );
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;
277  if ( mRasterTransparency )
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 
293 void 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 
313 void QgsSingleBandPseudoColorRenderer::legendSymbologyItems( QList< QPair< QString, QColor > > &symbolItems ) const
314 {
315  if ( mShader )
316  {
317  QgsRasterShaderFunction *shaderFunction = mShader->rasterShaderFunction();
318  if ( shaderFunction )
319  {
320  shaderFunction->legendSymbologyItems( symbolItems );
321  }
322  }
323 }
324 
326 {
327  QList<int> bandList;
328  if ( mBand != -1 )
329  {
330  bandList << mBand;
331  }
332  return bandList;
333 }
334 
335 void QgsSingleBandPseudoColorRenderer::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
336 {
337  QgsStringMap newProps = props;
338 
339  // create base structure
340  QgsRasterRenderer::toSld( doc, element, props );
341 
342  // look for RasterSymbolizer tag
343  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  {
372  switch ( rampShader->colorRampType() )
373  {
374  case ( QgsColorRampShader::Exact ):
375  rampType = QStringLiteral( "values" );
376  break;
378  rampType = QStringLiteral( "intervals" );
379  break;
381  rampType = QStringLiteral( "ramp" );
382  break;
383  }
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  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 }
virtual void setMinimumValue(double value)
Sets the minimum value for the raster shader.
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:40
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 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.
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.
const QgsRasterMinMaxOrigin & minMaxOrigin() const
Returns const reference to origin of min/max values.
QgsRasterMinMaxOrigin mMinMaxOrigin
Origin of min/max values.
void setClip(bool clip)
Sets whether the shader should not render values out of range.
QgsRasterShader * shader()
Returns the raster shader.
QMap< QString, QString > QgsStringMap
Definition: qgis.h:577
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.
bool usesTransparency() const
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:107
QList< QgsColorRampShader::ColorRampItem > colorRampItemList() const
Returns the custom colormap.
void setColorRampType(QgsColorRampShader::Type colorRampType)
Sets the color ramp type.
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.
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.
int band() const
Returns the band used by the renderer.
QgsRasterShaderFunction * rasterShaderFunction()
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 ].
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.
Type colorRampType() const
Returns the color ramp type.
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:586
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.
virtual void legendSymbologyItems(QList< QPair< QString, QColor > > &symbolItems) const
Returns legend symbology items if provided by renderer.
Assigns the color of the exact matching value in the color ramp item list.
virtual QgsRectangle extent() const
Gets the extent of the interface.
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.
void _writeXml(QDomDocument &doc, QDomElement &rasterRendererElem) const
Write upper class info into rasterrenderer element (called by writeXml method of subclasses) ...
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.
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.