QGIS API Documentation  3.20.0-Odense (decaadbb31)
qgscolorrampshader.cpp
Go to the documentation of this file.
1 /* **************************************************************************
2  qgscolorrampshader.cpp - description
3  -------------------
4 begin : Fri Dec 28 2007
5 copyright : (C) 2007 by Peter J. Ersts
6 email : [email protected]
7 
8 This class is based off of code that was originally written by Marco Hugentobler and
9 originally part of the larger QgsRasterLayer class
10 ****************************************************************************/
11 
12 /* **************************************************************************
13  * *
14  * This program is free software; you can redistribute it and/or modify *
15  * it under the terms of the GNU General Public License as published by *
16  * the Free Software Foundation; either version 2 of the License, or *
17  * (at your option) any later version. *
18  * *
19  ***************************************************************************/
20 
21 // Threshold for treating values as exact match.
22 // Set to 0.0 to support displaying small values (https://github.com/qgis/QGIS/issues/20706)
23 #define DOUBLE_DIFF_THRESHOLD 0.0 // 0.0000001
24 
25 #include "qgslogger.h"
26 #include "qgis.h"
27 #include "qgscolorramp.h"
28 #include "qgscolorrampshader.h"
29 #include "qgsrasterinterface.h"
30 #include "qgsrasterminmaxorigin.h"
31 #include "qgssymbollayerutils.h"
32 #include "qgsreadwritecontext.h"
34 
35 #include <cmath>
36 QgsColorRampShader::QgsColorRampShader( double minimumValue, double maximumValue, QgsColorRamp *colorRamp, Type type, ClassificationMode classificationMode )
37  : QgsRasterShaderFunction( minimumValue, maximumValue )
38  , mColorRampType( type )
39  , mClassificationMode( classificationMode )
40  , mLegendSettings( std::make_unique< QgsColorRampLegendNodeSettings >() )
41 {
42  QgsDebugMsgLevel( QStringLiteral( "called." ), 4 );
43 
44  setSourceColorRamp( colorRamp );
45 }
46 
48 
50  : QgsRasterShaderFunction( other )
51  , mColorRampType( other.mColorRampType )
52  , mClassificationMode( other.mClassificationMode )
53  , mLUT( other.mLUT )
54  , mLUTOffset( other.mLUTOffset )
55  , mLUTFactor( other.mLUTFactor )
56  , mLUTInitialized( other.mLUTInitialized )
57  , mClip( other.mClip )
58  , mLegendSettings( other.legendSettings() ? new QgsColorRampLegendNodeSettings( *other.legendSettings() ) : new QgsColorRampLegendNodeSettings() )
59 {
60  if ( auto *lSourceColorRamp = other.sourceColorRamp() )
61  mSourceColorRamp.reset( lSourceColorRamp->clone() );
62  mColorRampItemList = other.mColorRampItemList;
63 }
64 
66 {
67  QgsRasterShaderFunction::operator=( other );
68  if ( auto *lSourceColorRamp = other.sourceColorRamp() )
69  mSourceColorRamp.reset( lSourceColorRamp->clone() );
70  else
71  mSourceColorRamp.reset();
72 
73  mColorRampType = other.mColorRampType;
74  mClassificationMode = other.mClassificationMode;
75  mLUT = other.mLUT;
76  mLUTOffset = other.mLUTOffset;
77  mLUTFactor = other.mLUTFactor;
78  mLUTInitialized = other.mLUTInitialized;
79  mClip = other.mClip;
80  mColorRampItemList = other.mColorRampItemList;
81  mLegendSettings.reset( other.legendSettings() ? new QgsColorRampLegendNodeSettings( *other.legendSettings() ) : new QgsColorRampLegendNodeSettings() );
82  return *this;
83 }
84 
86 {
87  switch ( mColorRampType )
88  {
89  case Interpolated:
90  return QStringLiteral( "INTERPOLATED" );
91  case Discrete:
92  return QStringLiteral( "DISCRETE" );
93  case Exact:
94  return QStringLiteral( "EXACT" );
95  }
96  return QStringLiteral( "Unknown" );
97 }
98 
99 void QgsColorRampShader::setColorRampItemList( const QList<QgsColorRampShader::ColorRampItem> &list )
100 {
101  mColorRampItemList = list.toVector();
102  // Reset the look up table when the color ramp is changed
103  mLUTInitialized = false;
104  mLUT.clear();
105 }
106 
108 {
109  mColorRampType = colorRampType;
110 }
111 
113 {
114  return mColorRampItemList.isEmpty();
115 }
116 
117 void QgsColorRampShader::setColorRampType( const QString &type )
118 {
119  if ( type == QLatin1String( "INTERPOLATED" ) )
120  {
121  mColorRampType = Interpolated;
122  }
123  else if ( type == QLatin1String( "DISCRETE" ) )
124  {
125  mColorRampType = Discrete;
126  }
127  else
128  {
129  mColorRampType = Exact;
130  }
131 }
132 
134 {
135  return mSourceColorRamp.get();
136 }
137 
139 {
140  std::unique_ptr<QgsGradientColorRamp> ramp = std::make_unique< QgsGradientColorRamp >();
141  int count = mColorRampItemList.size();
142  if ( count == 0 )
143  {
144  const QColor none( 0, 0, 0, 0 );
145  ramp->setColor1( none );
146  ramp->setColor2( none );
147  }
148  else if ( count == 1 )
149  {
150  ramp->setColor1( mColorRampItemList[0].color );
151  ramp->setColor2( mColorRampItemList[0].color );
152  }
153  else
154  {
155  QgsGradientStopsList stops;
156  // minimum and maximum values can fall outside the range of the item list
157  const double min = minimumValue();
158  const double max = maximumValue();
159  for ( int i = 0; i < count; i++ )
160  {
161  double offset = ( mColorRampItemList[i].value - min ) / ( max - min );
162  if ( i == 0 )
163  {
164  ramp->setColor1( mColorRampItemList[i].color );
165  if ( offset <= 0.0 )
166  continue;
167  }
168  else if ( i == count - 1 )
169  {
170  ramp->setColor2( mColorRampItemList[i].color );
171  if ( offset >= 1.0 )
172  continue;
173  }
174  stops << QgsGradientStop( offset, mColorRampItemList[i].color );
175  }
176  ramp->setStops( stops );
177  }
178 
179  return ramp.release();
180 }
181 
183 {
184  mSourceColorRamp.reset( colorramp );
185 }
186 
187 void QgsColorRampShader::classifyColorRamp( const int classes, const int band, const QgsRectangle &extent, QgsRasterInterface *input )
188 {
189  if ( minimumValue() > maximumValue() )
190  return;
191 
192  bool discrete = colorRampType() == Discrete;
193 
194  QList<double> entryValues;
195  QVector<QColor> entryColors;
196 
197  double min = minimumValue();
198  double max = maximumValue();
199 
200  if ( minimumValue() == maximumValue() )
201  {
202  if ( sourceColorRamp() && sourceColorRamp()->count() > 1 )
203  {
204  entryValues.push_back( min );
205  if ( discrete )
206  entryValues.push_back( std::numeric_limits<double>::infinity() );
207  for ( int i = 0; i < entryValues.size(); ++i )
208  entryColors.push_back( sourceColorRamp()->color( sourceColorRamp()->value( i ) ) );
209  }
210  }
211  else if ( classificationMode() == Continuous )
212  {
213  if ( sourceColorRamp() && sourceColorRamp()->count() > 1 )
214  {
215  int numberOfEntries = sourceColorRamp()->count();
216  entryValues.reserve( numberOfEntries );
217  if ( discrete )
218  {
219  double intervalDiff = max - min;
220 
221  // remove last class when ColorRamp is gradient and discrete, as they are implemented with an extra stop
222  QgsGradientColorRamp *colorGradientRamp = dynamic_cast<QgsGradientColorRamp *>( sourceColorRamp() );
223  if ( colorGradientRamp && colorGradientRamp->isDiscrete() )
224  {
225  numberOfEntries--;
226  }
227  else
228  {
229  // if color ramp is continuous scale values to get equally distributed classes.
230  // Doesn't work perfectly when stops are non equally distributed.
231  intervalDiff *= ( numberOfEntries - 1 ) / static_cast<double>( numberOfEntries );
232  }
233 
234  // skip first value (always 0.0)
235  for ( int i = 1; i < numberOfEntries; ++i )
236  {
237  double value = sourceColorRamp()->value( i );
238  entryValues.push_back( min + value * intervalDiff );
239  }
240  entryValues.push_back( std::numeric_limits<double>::infinity() );
241  }
242  else
243  {
244  for ( int i = 0; i < numberOfEntries; ++i )
245  {
246  double value = sourceColorRamp()->value( i );
247  entryValues.push_back( min + value * ( max - min ) );
248  }
249  }
250  // for continuous mode take original color map colors
251  for ( int i = 0; i < numberOfEntries; ++i )
252  {
253  int idx = i;
254  entryColors.push_back( sourceColorRamp()->color( sourceColorRamp()->value( idx ) ) );
255  }
256  }
257  }
258  else // for other classification modes interpolate colors linearly
259  {
260  if ( classes < 2 )
261  return; // < 2 classes is not useful, shouldn't happen, but if it happens save it from crashing
262 
263  if ( classificationMode() == Quantile )
264  {
265  // Quantile
266  if ( band < 0 || !input )
267  return; // quantile classification requires a valid band, minMaxOrigin, and input
268 
269  double cut1 = std::numeric_limits<double>::quiet_NaN();
270  double cut2 = std::numeric_limits<double>::quiet_NaN();
271  // Note: the sample size in other parts of QGIS appears to be 25000, it is ten times here.
272  const int sampleSize = 250000 * 10;
273 
274  // set min and max from histogram, used later to calculate number of decimals to display
275  input->cumulativeCut( band, 0.0, 1.0, min, max, extent, sampleSize );
276 
277  entryValues.reserve( classes );
278  if ( discrete )
279  {
280  double intervalDiff = 1.0 / ( classes );
281  for ( int i = 1; i < classes; ++i )
282  {
283  input->cumulativeCut( band, 0.0, i * intervalDiff, cut1, cut2, extent, sampleSize );
284  entryValues.push_back( cut2 );
285  }
286  entryValues.push_back( std::numeric_limits<double>::infinity() );
287  }
288  else
289  {
290  double intervalDiff = 1.0 / ( classes - 1 );
291  for ( int i = 0; i < classes; ++i )
292  {
293  input->cumulativeCut( band, 0.0, i * intervalDiff, cut1, cut2, extent, sampleSize );
294  entryValues.push_back( cut2 );
295  }
296  }
297  }
298  else // EqualInterval
299  {
300  entryValues.reserve( classes );
301  if ( discrete )
302  {
303  // in discrete mode the lowest value is not an entry and the highest
304  // value is inf, there are ( numberOfEntries ) of which the first
305  // and last are not used.
306  double intervalDiff = ( max - min ) / ( classes );
307 
308  for ( int i = 1; i < classes; ++i )
309  {
310  entryValues.push_back( min + i * intervalDiff );
311  }
312  entryValues.push_back( std::numeric_limits<double>::infinity() );
313  }
314  else
315  {
316  //because the highest value is also an entry, there are (numberOfEntries - 1) intervals
317  double intervalDiff = ( max - min ) / ( classes - 1 );
318 
319  for ( int i = 0; i < classes; ++i )
320  {
321  entryValues.push_back( min + i * intervalDiff );
322  }
323  }
324  }
325 
326  if ( !sourceColorRamp() || sourceColorRamp()->count() == 1 )
327  {
328  //hard code color range from blue -> red (previous default)
329  int colorDiff = 0;
330  if ( classes != 0 )
331  {
332  colorDiff = ( int )( 255 / classes );
333  }
334 
335  entryColors.reserve( classes );
336  for ( int i = 0; i < classes; ++i )
337  {
338  QColor currentColor;
339  int idx = i;
340  currentColor.setRgb( colorDiff * idx, 0, 255 - colorDiff * idx );
341  entryColors.push_back( currentColor );
342  }
343  }
344  else
345  {
346  entryColors.reserve( classes );
347  for ( int i = 0; i < classes; ++i )
348  {
349  int idx = i;
350  entryColors.push_back( sourceColorRamp()->color( ( ( double ) idx ) / ( classes - 1 ) ) );
351  }
352  }
353  }
354 
355  QList<double>::const_iterator value_it = entryValues.constBegin();
356  QVector<QColor>::const_iterator color_it = entryColors.constBegin();
357 
358  // calculate a reasonable number of decimals to display
359  double maxabs = std::log10( std::max( std::fabs( max ), std::fabs( min ) ) );
360  int nDecimals = std::round( std::max( 3.0 + maxabs - std::log10( max - min ), maxabs <= 15.0 ? maxabs + 0.49 : 0.0 ) );
361 
362  QList<QgsColorRampShader::ColorRampItem> colorRampItems;
363  for ( ; value_it != entryValues.constEnd(); ++value_it, ++color_it )
364  {
365  QgsColorRampShader::ColorRampItem newColorRampItem;
366  newColorRampItem.value = *value_it;
367  newColorRampItem.color = *color_it;
368  newColorRampItem.label = QString::number( *value_it, 'g', nDecimals );
369  colorRampItems.append( newColorRampItem );
370  }
371 
372  std::sort( colorRampItems.begin(), colorRampItems.end() );
373  setColorRampItemList( colorRampItems );
374 }
375 
376 void QgsColorRampShader::classifyColorRamp( const int band, const QgsRectangle &extent, QgsRasterInterface *input )
377 {
378  classifyColorRamp( colorRampItemList().count(), band, extent, input );
379 }
380 
381 bool QgsColorRampShader::shade( double value, int *returnRedValue, int *returnGreenValue, int *returnBlueValue, int *returnAlphaValue ) const
382 {
383  if ( mColorRampItemList.isEmpty() )
384  {
385  return false;
386  }
387  if ( std::isnan( value ) || std::isinf( value ) )
388  return false;
389 
390  int colorRampItemListCount = mColorRampItemList.count();
391  const QgsColorRampShader::ColorRampItem *colorRampItems = mColorRampItemList.constData();
392  int idx;
393  if ( !mLUTInitialized )
394  {
395  // calculate LUT for faster index recovery
396  mLUTFactor = 1.0;
397  double minimumValue = colorRampItems[0].value;
398  mLUTOffset = minimumValue + DOUBLE_DIFF_THRESHOLD;
399  // Only make lut if at least 3 items, with 2 items the low and high cases handle both
400  if ( colorRampItemListCount >= 3 )
401  {
402  double rangeValue = colorRampItems[colorRampItemListCount - 2].value - minimumValue;
403  if ( rangeValue > 0 )
404  {
405  int lutSize = 256; // TODO: test if speed can be increased with a different LUT size
406  mLUTFactor = ( lutSize - 0.0000001 ) / rangeValue; // decrease slightly to make sure last LUT category is correct
407  idx = 0;
408  double val;
409  mLUT.reserve( lutSize );
410  for ( int i = 0; i < lutSize; i++ )
411  {
412  val = ( i / mLUTFactor ) + mLUTOffset;
413  while ( idx < colorRampItemListCount
414  && colorRampItems[idx].value - DOUBLE_DIFF_THRESHOLD < val )
415  {
416  idx++;
417  }
418  mLUT.emplace_back( idx );
419  }
420  }
421  }
422  mLUTInitialized = true;
423  }
424 
425  // overflow indicates that value > maximum value + DOUBLE_DIFF_THRESHOLD
426  // that way idx can point to the last valid item
427  bool overflow = false;
428 
429  // find index of the first ColorRampItem that is equal or higher to theValue
430  int lutIndex = ( value - mLUTOffset ) * mLUTFactor;
431  if ( value < mLUTOffset )
432  {
433  idx = 0;
434  }
435  else if ( static_cast< std::size_t>( lutIndex ) >= mLUT.size() )
436  {
437  idx = colorRampItemListCount - 1;
438  if ( colorRampItems[idx].value + DOUBLE_DIFF_THRESHOLD < value )
439  {
440  overflow = true;
441  }
442  }
443  else if ( lutIndex < 0 )
444  {
445  return false;
446  }
447  else
448  {
449  // get initial value from LUT
450  idx = mLUT[ lutIndex ];
451 
452  // check if it's correct and if not increase until correct
453  // the LUT is made in such a way the index is always correct or too low, never too high
454  while ( idx < colorRampItemListCount && colorRampItems[idx].value + DOUBLE_DIFF_THRESHOLD < value )
455  {
456  idx++;
457  }
458  if ( idx >= colorRampItemListCount )
459  {
460  idx = colorRampItemListCount - 1;
461  overflow = true;
462  }
463  }
464 
465  const QgsColorRampShader::ColorRampItem &currentColorRampItem = colorRampItems[idx];
466 
467  switch ( colorRampType() )
468  {
469  case Interpolated:
470  {
471  // Interpolate the color between two class breaks linearly.
472  if ( idx < 1 || overflow || currentColorRampItem.value - DOUBLE_DIFF_THRESHOLD <= value )
473  {
474  if ( mClip && ( overflow
475  || currentColorRampItem.value - DOUBLE_DIFF_THRESHOLD > value ) )
476  {
477  return false;
478  }
479  *returnRedValue = currentColorRampItem.color.red();
480  *returnGreenValue = currentColorRampItem.color.green();
481  *returnBlueValue = currentColorRampItem.color.blue();
482  *returnAlphaValue = currentColorRampItem.color.alpha();
483  return true;
484  }
485 
486  const QgsColorRampShader::ColorRampItem &previousColorRampItem = colorRampItems[idx - 1];
487 
488  float currentRampRange = currentColorRampItem.value - previousColorRampItem.value;
489  float offsetInRange = value - previousColorRampItem.value;
490  float scale = offsetInRange / currentRampRange;
491 
492  const QRgb c1 = previousColorRampItem.color.rgba();
493  const QRgb c2 = currentColorRampItem.color.rgba();
494 
495  *returnRedValue = qRed( c1 ) + static_cast< int >( ( qRed( c2 ) - qRed( c1 ) ) * scale );
496  *returnGreenValue = qGreen( c1 ) + static_cast< int >( ( qGreen( c2 ) - qGreen( c1 ) ) * scale );
497  *returnBlueValue = qBlue( c1 ) + static_cast< int >( ( qBlue( c2 ) - qBlue( c1 ) ) * scale );
498  *returnAlphaValue = qAlpha( c1 ) + static_cast< int >( ( qAlpha( c2 ) - qAlpha( c1 ) ) * scale );
499  return true;
500  };
501  case Discrete:
502  {
503  // Assign the color of the higher class for every pixel between two class breaks.
504  // NOTE: The implementation has always been different than the documentation,
505  // which said lower class before, see https://github.com/qgis/QGIS/issues/22009
506  if ( overflow )
507  {
508  return false;
509  }
510  *returnRedValue = currentColorRampItem.color.red();
511  *returnGreenValue = currentColorRampItem.color.green();
512  *returnBlueValue = currentColorRampItem.color.blue();
513  *returnAlphaValue = currentColorRampItem.color.alpha();
514  return true;
515  };
516  case Exact:
517  {
518  // Assign the color of the exact matching value in the color ramp item list
519  if ( !overflow && currentColorRampItem.value - DOUBLE_DIFF_THRESHOLD <= value )
520  {
521  *returnRedValue = currentColorRampItem.color.red();
522  *returnGreenValue = currentColorRampItem.color.green();
523  *returnBlueValue = currentColorRampItem.color.blue();
524  *returnAlphaValue = currentColorRampItem.color.alpha();
525  return true;
526  }
527  else
528  {
529  return false;
530  }
531  }
532  }
533  return false;
534 }
535 
536 bool QgsColorRampShader::shade( double redValue, double greenValue,
537  double blueValue, double alphaValue,
538  int *returnRedValue, int *returnGreenValue,
539  int *returnBlueValue, int *returnAlphaValue ) const
540 {
541  Q_UNUSED( redValue )
542  Q_UNUSED( greenValue )
543  Q_UNUSED( blueValue )
544  Q_UNUSED( alphaValue )
545 
546  *returnRedValue = 0;
547  *returnGreenValue = 0;
548  *returnBlueValue = 0;
549  *returnAlphaValue = 0;
550 
551  return false;
552 }
553 
554 void QgsColorRampShader::legendSymbologyItems( QList< QPair< QString, QColor > > &symbolItems ) const
555 {
556  QVector<QgsColorRampShader::ColorRampItem>::const_iterator colorRampIt = mColorRampItemList.constBegin();
557  for ( ; colorRampIt != mColorRampItemList.constEnd(); ++colorRampIt )
558  {
559  symbolItems.push_back( qMakePair( colorRampIt->label, colorRampIt->color ) );
560  }
561 }
562 
563 QDomElement QgsColorRampShader::writeXml( QDomDocument &doc, const QgsReadWriteContext &context ) const
564 {
565  QDomElement colorRampShaderElem = doc.createElement( QStringLiteral( "colorrampshader" ) );
566  colorRampShaderElem.setAttribute( QStringLiteral( "colorRampType" ), colorRampTypeAsQString() );
567  colorRampShaderElem.setAttribute( QStringLiteral( "classificationMode" ), classificationMode() );
568  colorRampShaderElem.setAttribute( QStringLiteral( "clip" ), clip() );
569  colorRampShaderElem.setAttribute( QStringLiteral( "minimumValue" ), mMinimumValue );
570  colorRampShaderElem.setAttribute( QStringLiteral( "maximumValue" ), mMaximumValue );
571  colorRampShaderElem.setAttribute( QStringLiteral( "labelPrecision" ), mLabelPrecision );
572 
573  // save source color ramp
574  if ( sourceColorRamp() )
575  {
576  QDomElement colorRampElem = QgsSymbolLayerUtils::saveColorRamp( QStringLiteral( "[source]" ), sourceColorRamp(), doc );
577  colorRampShaderElem.appendChild( colorRampElem );
578  }
579 
580  //items
581  QList<QgsColorRampShader::ColorRampItem> itemList = colorRampItemList();
582  QList<QgsColorRampShader::ColorRampItem>::const_iterator itemIt = itemList.constBegin();
583  for ( ; itemIt != itemList.constEnd(); ++itemIt )
584  {
585  QDomElement itemElem = doc.createElement( QStringLiteral( "item" ) );
586  itemElem.setAttribute( QStringLiteral( "label" ), itemIt->label );
587  itemElem.setAttribute( QStringLiteral( "value" ), QgsRasterBlock::printValue( itemIt->value ) );
588  itemElem.setAttribute( QStringLiteral( "color" ), itemIt->color.name() );
589  itemElem.setAttribute( QStringLiteral( "alpha" ), itemIt->color.alpha() );
590  colorRampShaderElem.appendChild( itemElem );
591  }
592 
593  if ( mLegendSettings )
594  mLegendSettings->writeXml( doc, colorRampShaderElem, context );
595 
596  return colorRampShaderElem;
597 }
598 
599 void QgsColorRampShader::readXml( const QDomElement &colorRampShaderElem, const QgsReadWriteContext &context )
600 {
601  // try to load color ramp (optional)
602  QDomElement sourceColorRampElem = colorRampShaderElem.firstChildElement( QStringLiteral( "colorramp" ) );
603  if ( !sourceColorRampElem.isNull() && sourceColorRampElem.attribute( QStringLiteral( "name" ) ) == QLatin1String( "[source]" ) )
604  {
605  setSourceColorRamp( QgsSymbolLayerUtils::loadColorRamp( sourceColorRampElem ) );
606  }
607 
608  setColorRampType( colorRampShaderElem.attribute( QStringLiteral( "colorRampType" ), QStringLiteral( "INTERPOLATED" ) ) );
609  setClassificationMode( static_cast< QgsColorRampShader::ClassificationMode >( colorRampShaderElem.attribute( QStringLiteral( "classificationMode" ), QStringLiteral( "1" ) ).toInt() ) );
610  setClip( colorRampShaderElem.attribute( QStringLiteral( "clip" ), QStringLiteral( "0" ) ) == QLatin1String( "1" ) );
611  setMinimumValue( colorRampShaderElem.attribute( QStringLiteral( "minimumValue" ) ).toDouble() );
612  setMaximumValue( colorRampShaderElem.attribute( QStringLiteral( "maximumValue" ) ).toDouble() );
613  setLabelPrecision( colorRampShaderElem.attribute( QStringLiteral( "labelPrecision" ), QStringLiteral( "6" ) ).toDouble() );
614 
615  QList<QgsColorRampShader::ColorRampItem> itemList;
616  QDomElement itemElem;
617  QString itemLabel;
618  double itemValue;
619  QColor itemColor;
620 
621  QDomNodeList itemNodeList = colorRampShaderElem.elementsByTagName( QStringLiteral( "item" ) );
622  itemList.reserve( itemNodeList.size() );
623  for ( int i = 0; i < itemNodeList.size(); ++i )
624  {
625  itemElem = itemNodeList.at( i ).toElement();
626  itemValue = itemElem.attribute( QStringLiteral( "value" ) ).toDouble();
627  itemLabel = itemElem.attribute( QStringLiteral( "label" ) );
628  itemColor.setNamedColor( itemElem.attribute( QStringLiteral( "color" ) ) );
629  itemColor.setAlpha( itemElem.attribute( QStringLiteral( "alpha" ), QStringLiteral( "255" ) ).toInt() );
630 
631  itemList.push_back( QgsColorRampShader::ColorRampItem( itemValue, itemColor, itemLabel ) );
632  }
633  setColorRampItemList( itemList );
634 
635  if ( !mLegendSettings )
636  mLegendSettings = std::make_unique< QgsColorRampLegendNodeSettings >();
637 
638  mLegendSettings->readXml( colorRampShaderElem, context );
639 }
640 
642 {
643  return mLegendSettings.get();
644 }
645 
647 {
648  if ( settings == mLegendSettings.get() )
649  return;
650  mLegendSettings.reset( settings );
651 }
Settings for a color ramp legend node.
A ramp shader will color a raster pixel based on a list of values ranges in a ramp.
~QgsColorRampShader() override
QList< QgsColorRampShader::ColorRampItem > colorRampItemList() const
Returns the custom colormap.
void legendSymbologyItems(QList< QPair< QString, QColor > > &symbolItems) const override
Returns legend symbology items if provided by renderer.
ClassificationMode classificationMode() const
Returns the classification mode.
const QgsColorRampLegendNodeSettings * legendSettings() const
Returns the color ramp shader legend settings.
bool isEmpty() const
Whether the color ramp contains any items.
Type colorRampType() const
Returns the color ramp type.
void setSourceColorRamp(QgsColorRamp *colorramp)
Set the source color ramp.
QgsColorRampShader & operator=(const QgsColorRampShader &other)
Assignment operator.
ClassificationMode
Classification modes used to create the color ramp shader.
@ Quantile
Uses quantile (i.e. equal pixel) count.
@ Continuous
Uses breaks from color palette.
void setClip(bool clip)
Sets whether the shader should not render values out of range.
bool clip() const
Returns whether the shader will clip values which are out of range.
QDomElement writeXml(QDomDocument &doc, const QgsReadWriteContext &context=QgsReadWriteContext()) const
Writes configuration to a new DOM element.
bool shade(double value, int *returnRedValue, int *returnGreenValue, int *returnBlueValue, int *returnAlphaValue) const override
Generates and new RGB value based on one input value.
QgsColorRamp * sourceColorRamp() const
Returns the source color ramp.
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 setClassificationMode(ClassificationMode classificationMode)
Sets classification mode.
void classifyColorRamp(int classes=0, int band=-1, const QgsRectangle &extent=QgsRectangle(), QgsRasterInterface *input=nullptr)
Classify color ramp shader.
void setColorRampItemList(const QList< QgsColorRampShader::ColorRampItem > &list)
Sets a custom colormap.
void setColorRampType(QgsColorRampShader::Type colorRampType)
Sets the color ramp type.
QgsColorRampShader(double minimumValue=0.0, double maximumValue=255.0, QgsColorRamp *colorRamp=nullptr, Type type=Interpolated, ClassificationMode classificationMode=Continuous)
Creates a new color ramp shader.
QString colorRampTypeAsQString() const
Returns the color ramp type as a string.
void readXml(const QDomElement &elem, const QgsReadWriteContext &context=QgsReadWriteContext())
Reads configuration from the given DOM element.
std::unique_ptr< QgsColorRamp > mSourceColorRamp
Source color ramp.
void setLegendSettings(QgsColorRampLegendNodeSettings *settings)
Sets the color ramp shader legend settings.
Abstract base class for color ramps.
Definition: qgscolorramp.h:32
virtual QColor color(double value) const =0
Returns the color corresponding to a specified value.
virtual int count() const =0
Returns number of defined colors, or -1 if undefined.
virtual double value(int index) const =0
Returns relative value between [0,1] of color at specified index.
Gradient color ramp, which smoothly interpolates between two colors and also supports optional extra ...
Definition: qgscolorramp.h:151
bool isDiscrete() const
Returns true if the gradient is using discrete interpolation, rather than smoothly interpolating betw...
Definition: qgscolorramp.h:221
Represents a color stop within a QgsGradientColorRamp color ramp.
Definition: qgscolorramp.h:113
static QString printValue(double value)
Print double value with all necessary significant digits.
Base class for processing filters like renderers, reprojector, resampler etc.
virtual void cumulativeCut(int bandNo, double lowerCount, double upperCount, double &lowerValue, double &upperValue, const QgsRectangle &extent=QgsRectangle(), int sampleSize=0)
Find values for cumulative pixel count cut.
The raster shade function applies a shader to a pixel at render time - typically used to render grays...
double mMinimumValue
User defineable minimum value for the shading function.
double maximumValue() const
Returns the minimum value for the raster shader.
double mMaximumValue
User defineable maximum value for the shading function.
void setLabelPrecision(int labelPrecision)
Sets label precision to labelPrecision.
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.
int mLabelPrecision
Label precision.
double minimumValue() const
Returns the maximum value for the raster shader.
The class is used as a container of context for various read/write operations on other objects.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
static QgsColorRamp * loadColorRamp(QDomElement &element)
Creates a color ramp from the settings encoded in an XML element.
static QDomElement saveColorRamp(const QString &name, QgsColorRamp *ramp, QDomDocument &doc)
Encodes a color ramp's settings to an XML element.
QList< QgsGradientStop > QgsGradientStopsList
List of gradient stops.
Definition: qgscolorramp.h:138
#define DOUBLE_DIFF_THRESHOLD
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39