QGIS API Documentation  2.5.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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 : ersts@amnh.org
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 
24 #include "qgscolorrampshader.h"
25 
26 #include <cmath>
27 
28 QgsColorRampShader::QgsColorRampShader( double theMinimumValue, double theMaximumValue ) : QgsRasterShaderFunction( theMinimumValue, theMaximumValue )
29 {
30  QgsDebugMsg( "called." );
31  mMaximumColorCacheSize = 1024; //good starting value
33 }
34 
36 {
37  switch ( mColorRampType )
38  {
39  case INTERPOLATED:
40  return QString( "INTERPOLATED" );
41  break;
42  case DISCRETE:
43  return QString( "DISCRETE" );
44  break;
45  case EXACT:
46  return QString( "EXACT" );
47  break;
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  {
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  {
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 ( 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  {
135  }
136  //Search back toward the beginning of the list
137  else
138  {
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  {
168  }
169  else if ( mCurrentColorRampItemIndex != 0 && ( theValue <= myColorRampItem.value || myTinyDiff <= DOUBLE_DIFF_THRESHOLD ) )
170  {
172  myCurrentRampRange = myColorRampItem.value - myPreviousColorRampItem.value;
173  myOffsetInRange = theValue - myPreviousColorRampItem.value;
174  double scale = myOffsetInRange / myCurrentRampRange;
175 
176  *theReturnRedValue = ( int )(( double ) myPreviousColorRampItem.color.red() + (( double )( myColorRampItem.color.red() - myPreviousColorRampItem.color.red() ) * scale ) ) ;
177  *theReturnGreenValue = ( int )(( double ) myPreviousColorRampItem.color.green() + (( double )( myColorRampItem.color.green() - myPreviousColorRampItem.color.green() ) * scale ) );
178  *theReturnBlueValue = ( int )(( double ) myPreviousColorRampItem.color.blue() + (( double )( myColorRampItem.color.blue() - myPreviousColorRampItem.color.blue() ) * scale ) );
179  *theReturnAlphaValue = ( int )(( double ) myPreviousColorRampItem.color.alpha() + (( 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  {
210  }
211  else
212  {
213  return false;
214  }
215  }
216 
217  return false;
218 }
219 
220 void QgsColorRampShader::setColorRampItemList( const QList<QgsColorRampShader::ColorRampItem>& theList )
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  {
241  }
242  else if ( theType == "DISCRETE" )
243  {
245  }
246  else
247  {
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  {
272  }
273  else if ( mCurrentColorRampItemIndex >= mColorRampItemList.size() )
274  {
276  }
277 
279  {
280  return exactColor( theValue, theReturnRedValue, theReturnGreenValue, theReturnBlueValue, theReturnAlphaValue );
281  }
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, int* theReturnRedValue, int* theReturnGreenValue, int*
292  theReturnBlueValue , int* theReturnAlphaValue )
293 {
294  Q_UNUSED( theRedValue );
295  Q_UNUSED( theGreenValue );
296  Q_UNUSED( theBlueValue );
297  Q_UNUSED( theAlphaValue );
298 
299  *theReturnRedValue = 0;
300  *theReturnGreenValue = 0;
301  *theReturnBlueValue = 0;
302  *theReturnAlphaValue = 0;
303 
304  return false;
305 }
306 
307 void QgsColorRampShader::legendSymbologyItems( QList< QPair< QString, QColor > >& symbolItems ) const
308 {
309  QList<QgsColorRampShader::ColorRampItem>::const_iterator colorRampIt = mColorRampItemList.constBegin();
310  for ( ; colorRampIt != mColorRampItemList.constEnd(); ++colorRampIt )
311  {
312  symbolItems.push_back( qMakePair( colorRampIt->label, colorRampIt->color ) );
313  }
314 }
void legendSymbologyItems(QList< QPair< QString, QColor > > &symbolItems) const
#define QgsDebugMsg(str)
Definition: qgslogger.h:36
QgsColorRampShader(double theMinimumValue=0.0, double theMaximumValue=255.0)
QList< QgsColorRampShader::ColorRampItem > mColorRampItemList
This vector holds the information for classification based on values.
void setColorRampItemList(const QList< QgsColorRampShader::ColorRampItem > &theList)
Set custom colormap.
QMap< double, QColor > mColorCache
Cache of values that have already been looked up.
The raster shade function applies a shader to a pixel at render time - typically used to render grays...
void setColorRampType(QgsColorRampShader::ColorRamp_TYPE theColorRampType)
Set the color ramp type.
QString colorRampTypeAsQString()
Get the color ramp type as a string.
QgsColorRampShader::ColorRamp_TYPE mColorRampType
The color ramp type.
bool shade(double, int *, int *, int *, int *)
Generates and new RGB value based on one input value.
bool mClip
Do not render values out of range.
bool exactColor(double, int *, int *, int *, int *)
Gets the color for a pixel value from the classification vector mValueClassification.
bool interpolatedColor(double, int *, int *, int *, int *)
Gets the color for a pixel value from the classification vector mValueClassification.
#define DOUBLE_DIFF_THRESHOLD
int mCurrentColorRampItemIndex
Current index from which to start searching the color table.
int mMaximumColorCacheSize
Maximum size of the color cache.
bool discreteColor(double, int *, int *, int *, int *)
Gets the color for a pixel value from the classification vector mValueClassification.