QGIS API Documentation  2.14.0-Essen
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 #define DOUBLE_DIFF_THRESHOLD 0.0000001
21 
22 #include "qgslogger.h"
23 #include "qgis.h"
24 #include "qgscolorrampshader.h"
25 
26 #include <cmath>
27 
28 QgsColorRampShader::QgsColorRampShader( double theMinimumValue, double theMaximumValue )
29  : QgsRasterShaderFunction( theMinimumValue, theMaximumValue )
30  , mColorRampType( INTERPOLATED )
31  , mClip( false )
32 {
33  QgsDebugMsgLevel( "called.", 4 );
34  mMaximumColorCacheSize = 1024; //good starting value
35  mCurrentColorRampItemIndex = 0;
36 }
37 
39 {
40  switch ( mColorRampType )
41  {
42  case INTERPOLATED:
43  return QString( "INTERPOLATED" );
44  case DISCRETE:
45  return QString( "DISCRETE" );
46  case EXACT:
47  return QString( "EXACT" );
48  }
49  return QString( "Unknown" );
50 }
51 
52 bool QgsColorRampShader::discreteColor( double theValue, int* theReturnRedValue, int* theReturnGreenValue, int* theReturnBlueValue, int* theReturnAlphaValue )
53 {
54  int myColorRampItemCount = mColorRampItemList.count();
55  if ( myColorRampItemCount <= 0 )
56  {
57  return false;
58  }
59 
60  double myTinyDiff = 0.0;
61  QgsColorRampShader::ColorRampItem myColorRampItem;
62  while ( mCurrentColorRampItemIndex >= 0 && mCurrentColorRampItemIndex < myColorRampItemCount )
63  {
64  //Start searching from the last index - assumtion is that neighboring pixels tend to be similar values
65  myColorRampItem = mColorRampItemList.value( mCurrentColorRampItemIndex );
66  myTinyDiff = qAbs( theValue - myColorRampItem.value );
67  //If the previous entry is less, then search closer to the top of the list (assumes mColorRampItemList is sorted)
68  if ( mCurrentColorRampItemIndex != 0 &&
69  theValue <= mColorRampItemList.at( mCurrentColorRampItemIndex - 1 ).value )
70  {
71  mCurrentColorRampItemIndex--;
72  }
73  else if ( theValue <= myColorRampItem.value || myTinyDiff <= DOUBLE_DIFF_THRESHOLD )
74  {
75  *theReturnRedValue = myColorRampItem.color.red();
76  *theReturnGreenValue = myColorRampItem.color.green();
77  *theReturnBlueValue = myColorRampItem.color.blue();
78  *theReturnAlphaValue = myColorRampItem.color.alpha();
79  //Cache the shaded value
80  if ( mMaximumColorCacheSize >= mColorCache.size() )
81  {
82  mColorCache.insert( theValue, myColorRampItem.color );
83  }
84  return true;
85  }
86  //Search deeper into the color ramp list
87  else
88  {
89  mCurrentColorRampItemIndex++;
90  }
91  }
92 
93  return false; // value not found
94 }
95 
96 bool QgsColorRampShader::exactColor( double theValue, int* theReturnRedValue, int* theReturnGreenValue, int* theReturnBlueValue, int *theReturnAlphaValue )
97 {
98  int myColorRampItemCount = mColorRampItemList.count();
99  if ( myColorRampItemCount <= 0 )
100  {
101  return false;
102  }
103 
104  double myTinyDiff = 0.0;
105  QgsColorRampShader::ColorRampItem myColorRampItem;
106  while ( mCurrentColorRampItemIndex >= 0 && mCurrentColorRampItemIndex < myColorRampItemCount )
107  {
108  //Start searching from the last index - assumtion is that neighboring pixels tend to be similar values
109  myColorRampItem = mColorRampItemList.value( mCurrentColorRampItemIndex );
110  myTinyDiff = qAbs( theValue - myColorRampItem.value );
111  if ( qgsDoubleNear( theValue, myColorRampItem.value ) || myTinyDiff <= DOUBLE_DIFF_THRESHOLD )
112  {
113  *theReturnRedValue = myColorRampItem.color.red();
114  *theReturnGreenValue = myColorRampItem.color.green();
115  *theReturnBlueValue = myColorRampItem.color.blue();
116  *theReturnAlphaValue = myColorRampItem.color.alpha();
117  //Cache the shaded value
118  if ( mMaximumColorCacheSize >= mColorCache.size() )
119  {
120  mColorCache.insert( theValue, myColorRampItem.color );
121  }
122  return true;
123  }
124  //pixel value sits between ramp entries so bail
125  else if ( mCurrentColorRampItemIndex != myColorRampItemCount - 1 &&
126  theValue > myColorRampItem.value && theValue < mColorRampItemList.at(
127  mCurrentColorRampItemIndex + 1 ).value )
128  {
129  return false;
130  }
131  //Search deeper into the color ramp list
132  else if ( theValue > myColorRampItem.value )
133  {
134  mCurrentColorRampItemIndex++;
135  }
136  //Search back toward the beginning of the list
137  else
138  {
139  mCurrentColorRampItemIndex--;
140  }
141  }
142 
143  return false; // value not found
144 }
145 
146 bool QgsColorRampShader::interpolatedColor( double theValue, int*
147  theReturnRedValue, int* theReturnGreenValue, int* theReturnBlueValue, int* theReturnAlphaValue )
148 {
149  int myColorRampItemCount = mColorRampItemList.count();
150  if ( myColorRampItemCount <= 0 )
151  {
152  return false;
153  }
154 
155  double myTinyDiff = 0.0;
156  double myCurrentRampRange; //difference between two consecutive entry values
157  double myOffsetInRange; //difference between the previous entry value and value
158  QgsColorRampShader::ColorRampItem myColorRampItem;
159  while ( mCurrentColorRampItemIndex >= 0 && mCurrentColorRampItemIndex < myColorRampItemCount )
160  {
161  //Start searching from the last index - assumtion is that neighboring pixels tend to be similar values
162  myColorRampItem = mColorRampItemList.value( mCurrentColorRampItemIndex );
163  myTinyDiff = qAbs( theValue - myColorRampItem.value );
164  //If the previous entry is less, then search closer to the top of the list (assumes mColorRampItemList is sorted)
165  if ( mCurrentColorRampItemIndex != 0 && theValue <= mColorRampItemList.at( mCurrentColorRampItemIndex - 1 ).value )
166  {
167  mCurrentColorRampItemIndex--;
168  }
169  else if ( mCurrentColorRampItemIndex != 0 && ( theValue <= myColorRampItem.value || myTinyDiff <= DOUBLE_DIFF_THRESHOLD ) )
170  {
171  QgsColorRampShader::ColorRampItem myPreviousColorRampItem = mColorRampItemList.value( mCurrentColorRampItemIndex - 1 );
172  myCurrentRampRange = myColorRampItem.value - myPreviousColorRampItem.value;
173  myOffsetInRange = theValue - myPreviousColorRampItem.value;
174  double scale = myOffsetInRange / myCurrentRampRange;
175 
176  *theReturnRedValue = static_cast< int >( static_cast< double >( myPreviousColorRampItem.color.red() ) + ( static_cast< double >( myColorRampItem.color.red() - myPreviousColorRampItem.color.red() ) * scale ) );
177  *theReturnGreenValue = static_cast< int >( static_cast< double >( myPreviousColorRampItem.color.green() ) + ( static_cast< double >( myColorRampItem.color.green() - myPreviousColorRampItem.color.green() ) * scale ) );
178  *theReturnBlueValue = static_cast< int >( static_cast< double >( myPreviousColorRampItem.color.blue() ) + ( static_cast< double >( myColorRampItem.color.blue() - myPreviousColorRampItem.color.blue() ) * scale ) );
179  *theReturnAlphaValue = static_cast< int >( static_cast< double >( myPreviousColorRampItem.color.alpha() ) + ( static_cast< double >( myColorRampItem.color.alpha() - myPreviousColorRampItem.color.alpha() ) * scale ) );
180  if ( mMaximumColorCacheSize >= mColorCache.size() )
181  {
182  QColor myNewColor( *theReturnRedValue, *theReturnGreenValue, *theReturnBlueValue, *theReturnAlphaValue );
183  mColorCache.insert( theValue, myNewColor );
184  }
185  return true;
186  }
187  // Values outside total range are rendered if mClip is false
188  else if (( mCurrentColorRampItemIndex == 0 && ( myTinyDiff <= DOUBLE_DIFF_THRESHOLD || ( !mClip && theValue <= myColorRampItem.value ) ) )
189  || ( mCurrentColorRampItemIndex == myColorRampItemCount - 1 && ( myTinyDiff <= DOUBLE_DIFF_THRESHOLD || ( !mClip && theValue >= myColorRampItem.value ) ) ) )
190  {
191  //QgsColorRampShader::ColorRampItem myPreviousColorRampItem = mColorRampItemList.value( mCurrentColorRampItemIndex - 1 );
192  //myCurrentRampRange = myColorRampItem.value - myPreviousColorRampItem.value;
193  //myOffsetInRange = theValue - myPreviousColorRampItem.value;
194 
195  *theReturnRedValue = myColorRampItem.color.red();
196  *theReturnGreenValue = myColorRampItem.color.green();
197  *theReturnBlueValue = myColorRampItem.color.blue();
198  *theReturnAlphaValue = myColorRampItem.color.alpha();
199  if ( mMaximumColorCacheSize >= mColorCache.size() )
200  {
201  QColor myNewColor( *theReturnRedValue, *theReturnGreenValue, *theReturnBlueValue, *theReturnAlphaValue );
202  mColorCache.insert( theValue, myNewColor );
203  }
204  return true;
205  }
206  //Search deeper into the color ramp list
207  else if ( theValue > myColorRampItem.value )
208  {
209  mCurrentColorRampItemIndex++;
210  }
211  else
212  {
213  return false;
214  }
215  }
216 
217  return false;
218 }
219 
221 {
222  mColorRampItemList = theList;
223  //Clear the cache
224  mColorCache.clear();
225 }
226 
228 {
229  //When the ramp type changes we need to clear out the cache
230  mColorCache.clear();
231  mColorRampType = theColorRampType;
232 }
233 
235 {
236  //When the type of the ramp changes we need to clear out the cache
237  mColorCache.clear();
238  if ( theType == "INTERPOLATED" )
239  {
240  mColorRampType = INTERPOLATED;
241  }
242  else if ( theType == "DISCRETE" )
243  {
244  mColorRampType = DISCRETE;
245  }
246  else
247  {
248  mColorRampType = EXACT;
249  }
250 }
251 
252 bool QgsColorRampShader::shade( double theValue, int* theReturnRedValue, int* theReturnGreenValue, int* theReturnBlueValue, int *theReturnAlphaValue )
253 {
254 
255  //Get the shaded value from the cache if it exists already
256  QColor myColor = mColorCache.value( theValue );
257  if ( myColor.isValid() )
258  {
259  *theReturnRedValue = myColor.red();
260  *theReturnGreenValue = myColor.green();
261  *theReturnBlueValue = myColor.blue();
262  *theReturnAlphaValue = myColor.alpha();
263  return true;
264  }
265 
266  //pixel value not in cache so generate new value
267 
268  //Check to be sure mCurrentColorRampItemIndex is within the valid range.
269  if ( mCurrentColorRampItemIndex < 0 )
270  {
271  mCurrentColorRampItemIndex = 0;
272  }
273  else if ( mCurrentColorRampItemIndex >= mColorRampItemList.size() )
274  {
275  mCurrentColorRampItemIndex = mColorRampItemList.size() - 1;
276  }
277 
278  if ( QgsColorRampShader::EXACT == mColorRampType )
279  {
280  return exactColor( theValue, theReturnRedValue, theReturnGreenValue, theReturnBlueValue, theReturnAlphaValue );
281  }
282  else if ( QgsColorRampShader::INTERPOLATED == mColorRampType )
283  {
284  return interpolatedColor( theValue, theReturnRedValue, theReturnGreenValue, theReturnBlueValue, theReturnAlphaValue );
285  }
286 
287  return discreteColor( theValue, theReturnRedValue, theReturnGreenValue, theReturnBlueValue, theReturnAlphaValue );
288 }
289 
290 bool QgsColorRampShader::shade( double theRedValue, double theGreenValue,
291  double theBlueValue, double theAlphaValue,
292  int* theReturnRedValue, int* theReturnGreenValue,
293  int* theReturnBlueValue, int* theReturnAlphaValue )
294 {
295  Q_UNUSED( theRedValue );
296  Q_UNUSED( theGreenValue );
297  Q_UNUSED( theBlueValue );
298  Q_UNUSED( theAlphaValue );
299 
300  *theReturnRedValue = 0;
301  *theReturnGreenValue = 0;
302  *theReturnBlueValue = 0;
303  *theReturnAlphaValue = 0;
304 
305  return false;
306 }
307 
309 {
310  QList<QgsColorRampShader::ColorRampItem>::const_iterator colorRampIt = mColorRampItemList.constBegin();
311  for ( ; colorRampIt != mColorRampItemList.constEnd(); ++colorRampIt )
312  {
313  symbolItems.push_back( qMakePair( colorRampIt->label, colorRampIt->color ) );
314  }
315 }
QgsColorRampShader(double theMinimumValue=0.0, double theMaximumValue=255.0)
void setColorRampItemList(const QList< QgsColorRampShader::ColorRampItem > &theList)
Set custom colormap.
void clear()
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Definition: qgis.h:285
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:34
int red() const
bool shade(double, int *, int *, int *, int *) override
Generates and new RGB value based on one input value.
The raster shade function applies a shader to a pixel at render time - typically used to render grays...
void legendSymbologyItems(QList< QPair< QString, QColor > > &symbolItems) const override
int alpha() const
void setColorRampType(QgsColorRampShader::ColorRamp_TYPE theColorRampType)
Set the color ramp type.
int green() const
QString colorRampTypeAsQString()
Get the color ramp type as a string.
int blue() const
#define DOUBLE_DIFF_THRESHOLD
iterator insert(const Key &key, const T &value)
int size() const
bool isValid() const
const T value(const Key &key) const