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