QGIS API Documentation  master-6227475
src/core/raster/qgscolorrampshader.cpp
Go to the documentation of this file.
00001 /* **************************************************************************
00002                 qgscolorrampshader.cpp -  description
00003                        -------------------
00004 begin                : Fri Dec 28 2007
00005 copyright            : (C) 2007 by Peter J. Ersts
00006 email                : ersts@amnh.org
00007 
00008 This class is based off of code that was originally written by Marco Hugentobler and
00009 originally part of the larger QgsRasterLayer class
00010 ****************************************************************************/
00011 
00012 /* **************************************************************************
00013  *                                                                         *
00014  *   This program is free software; you can redistribute it and/or modify  *
00015  *   it under the terms of the GNU General Public License as published by  *
00016  *   the Free Software Foundation; either version 2 of the License, or     *
00017  *   (at your option) any later version.                                   *
00018  *                                                                         *
00019  ***************************************************************************/
00020 #define DOUBLE_DIFF_THRESHOLD 0.0000001
00021 
00022 #include "qgslogger.h"
00023 
00024 #include "qgscolorrampshader.h"
00025 
00026 #include <cmath>
00027 
00028 QgsColorRampShader::QgsColorRampShader( double theMinimumValue, double theMaximumValue ) : QgsRasterShaderFunction( theMinimumValue, theMaximumValue )
00029 {
00030   QgsDebugMsg( "called." );
00031   mMaximumColorCacheSize = 1024; //good starting value
00032   mCurrentColorRampItemIndex = 0;
00033 }
00034 
00035 QString QgsColorRampShader::colorRampTypeAsQString()
00036 {
00037   switch ( mColorRampType )
00038   {
00039     case INTERPOLATED:
00040       return QString( "INTERPOLATED" );
00041       break;
00042     case DISCRETE:
00043       return QString( "DISCRETE" );
00044       break;
00045     case EXACT:
00046       return QString( "EXACT" );
00047       break;
00048   }
00049   return QString( "Unknown" );
00050 }
00051 
00052 bool QgsColorRampShader::discreteColor( double theValue, int* theReturnRedValue, int* theReturnGreenValue, int* theReturnBlueValue, int* theReturnAlphaValue )
00053 {
00054   int myColorRampItemCount = mColorRampItemList.count();
00055   if ( myColorRampItemCount <= 0 )
00056   {
00057     return false;
00058   }
00059 
00060   double myTinyDiff = 0.0;
00061   QgsColorRampShader::ColorRampItem myColorRampItem;
00062   while ( mCurrentColorRampItemIndex >= 0 && mCurrentColorRampItemIndex < myColorRampItemCount )
00063   {
00064     //Start searching from the last index - assumtion is that neighboring pixels tend to be similar values
00065     myColorRampItem = mColorRampItemList.value( mCurrentColorRampItemIndex );
00066     myTinyDiff = qAbs( theValue - myColorRampItem.value );
00067     //If the previous entry is less, then search closer to the top of the list (assumes mColorRampItemList is sorted)
00068     if ( mCurrentColorRampItemIndex != 0 &&
00069          theValue <= mColorRampItemList.at( mCurrentColorRampItemIndex - 1 ).value )
00070     {
00071       mCurrentColorRampItemIndex--;
00072     }
00073     else if ( theValue <= myColorRampItem.value || myTinyDiff <= DOUBLE_DIFF_THRESHOLD )
00074     {
00075       *theReturnRedValue = myColorRampItem.color.red();
00076       *theReturnGreenValue = myColorRampItem.color.green();
00077       *theReturnBlueValue = myColorRampItem.color.blue();
00078       *theReturnAlphaValue = myColorRampItem.color.alpha();
00079       //Cache the shaded value
00080       if ( mMaximumColorCacheSize >= mColorCache.size() )
00081       {
00082         mColorCache.insert( theValue, myColorRampItem.color );
00083       }
00084       return true;
00085     }
00086     //Search deeper into the color ramp list
00087     else
00088     {
00089       mCurrentColorRampItemIndex++;
00090     }
00091   }
00092 
00093   return false; // value not found
00094 }
00095 
00096 bool QgsColorRampShader::exactColor( double theValue, int* theReturnRedValue, int* theReturnGreenValue, int* theReturnBlueValue , int *theReturnAlphaValue )
00097 {
00098   int myColorRampItemCount = mColorRampItemList.count();
00099   if ( myColorRampItemCount <= 0 )
00100   {
00101     return false;
00102   }
00103 
00104   double myTinyDiff = 0.0;
00105   QgsColorRampShader::ColorRampItem myColorRampItem;
00106   while ( mCurrentColorRampItemIndex >= 0 && mCurrentColorRampItemIndex < myColorRampItemCount )
00107   {
00108     //Start searching from the last index - assumtion is that neighboring pixels tend to be similar values
00109     myColorRampItem = mColorRampItemList.value( mCurrentColorRampItemIndex );
00110     myTinyDiff = qAbs( theValue - myColorRampItem.value );
00111     if ( theValue == myColorRampItem.value || myTinyDiff <= DOUBLE_DIFF_THRESHOLD )
00112     {
00113       *theReturnRedValue = myColorRampItem.color.red();
00114       *theReturnGreenValue = myColorRampItem.color.green();
00115       *theReturnBlueValue = myColorRampItem.color.blue();
00116       *theReturnAlphaValue = myColorRampItem.color.alpha();
00117       //Cache the shaded value
00118       if ( mMaximumColorCacheSize >= mColorCache.size() )
00119       {
00120         mColorCache.insert( theValue, myColorRampItem.color );
00121       }
00122       return true;
00123     }
00124     //pixel value sits between ramp entries so bail
00125     else if ( mCurrentColorRampItemIndex != myColorRampItemCount - 1 &&
00126               theValue > myColorRampItem.value && theValue < mColorRampItemList.at(
00127                 mCurrentColorRampItemIndex + 1 ).value )
00128     {
00129       return false;
00130     }
00131     //Search deeper into the color ramp list
00132     else if ( theValue > myColorRampItem.value )
00133     {
00134       mCurrentColorRampItemIndex++;
00135     }
00136     //Search back toward the beginning of the list
00137     else
00138     {
00139       mCurrentColorRampItemIndex--;
00140     }
00141   }
00142 
00143   return false; // value not found
00144 }
00145 
00146 bool QgsColorRampShader::interpolatedColor( double theValue, int*
00147     theReturnRedValue, int* theReturnGreenValue, int* theReturnBlueValue , int* theReturnAlphaValue )
00148 {
00149   int myColorRampItemCount = mColorRampItemList.count();
00150   if ( myColorRampItemCount <= 0 )
00151   {
00152     return false;
00153   }
00154 
00155   double myTinyDiff = 0.0;
00156   double myCurrentRampRange; //difference between two consecutive entry values
00157   double myOffsetInRange; //difference between the previous entry value and value
00158   QgsColorRampShader::ColorRampItem myColorRampItem;
00159   while ( mCurrentColorRampItemIndex >= 0 && mCurrentColorRampItemIndex < myColorRampItemCount )
00160   {
00161     //Start searching from the last index - assumtion is that neighboring pixels tend to be similar values
00162     myColorRampItem = mColorRampItemList.value( mCurrentColorRampItemIndex );
00163     myTinyDiff = qAbs( theValue - myColorRampItem.value );
00164     //If the previous entry is less, then search closer to the top of the list (assumes mColorRampItemList is sorted)
00165     if ( mCurrentColorRampItemIndex != 0 && theValue <= mColorRampItemList.at( mCurrentColorRampItemIndex - 1 ).value )
00166     {
00167       mCurrentColorRampItemIndex--;
00168     }
00169     else if ( mCurrentColorRampItemIndex != 0 && ( theValue <= myColorRampItem.value || myTinyDiff <= DOUBLE_DIFF_THRESHOLD ) )
00170     {
00171       QgsColorRampShader::ColorRampItem myPreviousColorRampItem = mColorRampItemList.value( mCurrentColorRampItemIndex - 1 );
00172       myCurrentRampRange = myColorRampItem.value - myPreviousColorRampItem.value;
00173       myOffsetInRange = theValue - myPreviousColorRampItem.value;
00174       double scale = myOffsetInRange / myCurrentRampRange;
00175 
00176       *theReturnRedValue = ( int )(( double ) myPreviousColorRampItem.color.red() + (( double )( myColorRampItem.color.red() - myPreviousColorRampItem.color.red() ) * scale ) ) ;
00177       *theReturnGreenValue = ( int )(( double ) myPreviousColorRampItem.color.green() + (( double )( myColorRampItem.color.green() - myPreviousColorRampItem.color.green() ) * scale ) );
00178       *theReturnBlueValue = ( int )(( double ) myPreviousColorRampItem.color.blue() + (( double )( myColorRampItem.color.blue() - myPreviousColorRampItem.color.blue() ) * scale ) );
00179       *theReturnAlphaValue = ( int )(( double ) myPreviousColorRampItem.color.alpha() + (( double )( myColorRampItem.color.alpha() - myPreviousColorRampItem.color.alpha() ) * scale ) );
00180       if ( mMaximumColorCacheSize >= mColorCache.size() )
00181       {
00182         QColor myNewColor( *theReturnRedValue, *theReturnGreenValue, *theReturnBlueValue, *theReturnAlphaValue );
00183         mColorCache.insert( theValue, myNewColor );
00184       }
00185       return true;
00186     }
00187     // Values outside total range are rendered if mClip is false
00188     else if (( mCurrentColorRampItemIndex == 0 && ( myTinyDiff <= DOUBLE_DIFF_THRESHOLD || ( !mClip && theValue <= myColorRampItem.value ) ) )
00189              || ( mCurrentColorRampItemIndex == myColorRampItemCount - 1 && ( myTinyDiff <= DOUBLE_DIFF_THRESHOLD || ( !mClip && theValue >= myColorRampItem.value ) ) ) )
00190     {
00191       //QgsColorRampShader::ColorRampItem myPreviousColorRampItem = mColorRampItemList.value( mCurrentColorRampItemIndex - 1 );
00192       //myCurrentRampRange = myColorRampItem.value - myPreviousColorRampItem.value;
00193       //myOffsetInRange = theValue - myPreviousColorRampItem.value;
00194 
00195       *theReturnRedValue = myColorRampItem.color.red();
00196       *theReturnGreenValue = myColorRampItem.color.green();
00197       *theReturnBlueValue = myColorRampItem.color.blue();
00198       *theReturnAlphaValue = myColorRampItem.color.alpha();
00199       if ( mMaximumColorCacheSize >= mColorCache.size() )
00200       {
00201         QColor myNewColor( *theReturnRedValue, *theReturnGreenValue, *theReturnBlueValue, *theReturnAlphaValue );
00202         mColorCache.insert( theValue, myNewColor );
00203       }
00204       return true;
00205     }
00206     //Search deeper into the color ramp list
00207     else if ( theValue > myColorRampItem.value )
00208     {
00209       mCurrentColorRampItemIndex++;
00210     }
00211     else
00212     {
00213       return false;
00214     }
00215   }
00216 
00217   return false;
00218 }
00219 
00220 void QgsColorRampShader::setColorRampItemList( const QList<QgsColorRampShader::ColorRampItem>& theList )
00221 {
00222   mColorRampItemList = theList;
00223   //Clear the cache
00224   mColorCache.clear();
00225 }
00226 
00227 void QgsColorRampShader::setColorRampType( QgsColorRampShader::ColorRamp_TYPE theColorRampType )
00228 {
00229   //When the ramp type changes we need to clear out the cache
00230   mColorCache.clear();
00231   mColorRampType = theColorRampType;
00232 }
00233 
00234 void QgsColorRampShader::setColorRampType( QString theType )
00235 {
00236   //When the type of the ramp changes we need to clear out the cache
00237   mColorCache.clear();
00238   if ( theType == "INTERPOLATED" )
00239   {
00240     mColorRampType = INTERPOLATED;
00241   }
00242   else if ( theType == "DISCRETE" )
00243   {
00244     mColorRampType = DISCRETE;
00245   }
00246   else
00247   {
00248     mColorRampType = EXACT;
00249   }
00250 }
00251 
00252 bool QgsColorRampShader::shade( double theValue, int* theReturnRedValue, int* theReturnGreenValue, int* theReturnBlueValue , int *theReturnAlphaValue )
00253 {
00254 
00255   //Get the shaded value from the cache if it exists already
00256   QColor myColor = mColorCache.value( theValue );
00257   if ( myColor.isValid() )
00258   {
00259     *theReturnRedValue = myColor.red();
00260     *theReturnGreenValue = myColor.green();
00261     *theReturnBlueValue = myColor.blue();
00262     *theReturnAlphaValue = myColor.alpha();
00263     return true;
00264   }
00265 
00266   //pixel value not in cache so generate new value
00267 
00268   //Check to be sure mCurrentColorRampItemIndex is within the valid range.
00269   if ( mCurrentColorRampItemIndex < 0 )
00270   {
00271     mCurrentColorRampItemIndex = 0;
00272   }
00273   else if ( mCurrentColorRampItemIndex >= mColorRampItemList.size() )
00274   {
00275     mCurrentColorRampItemIndex = mColorRampItemList.size() - 1;
00276   }
00277 
00278   if ( QgsColorRampShader::EXACT == mColorRampType )
00279   {
00280     return exactColor( theValue, theReturnRedValue, theReturnGreenValue, theReturnBlueValue, theReturnAlphaValue );
00281   }
00282   else if ( QgsColorRampShader::INTERPOLATED == mColorRampType )
00283   {
00284     return interpolatedColor( theValue, theReturnRedValue, theReturnGreenValue, theReturnBlueValue, theReturnAlphaValue );
00285   }
00286 
00287   return discreteColor( theValue, theReturnRedValue, theReturnGreenValue, theReturnBlueValue, theReturnAlphaValue );
00288 }
00289 
00290 bool QgsColorRampShader::shade( double theRedValue, double theGreenValue,
00291                                 double theBlueValue, double theAlphaValue, int* theReturnRedValue, int* theReturnGreenValue, int*
00292                                 theReturnBlueValue , int* theReturnAlphaValue )
00293 {
00294   Q_UNUSED( theRedValue );
00295   Q_UNUSED( theGreenValue );
00296   Q_UNUSED( theBlueValue );
00297   Q_UNUSED( theAlphaValue );
00298 
00299   *theReturnRedValue = 0;
00300   *theReturnGreenValue = 0;
00301   *theReturnBlueValue = 0;
00302   *theReturnAlphaValue = 0;
00303 
00304   return false;
00305 }
00306 
00307 void QgsColorRampShader::legendSymbologyItems( QList< QPair< QString, QColor > >& symbolItems ) const
00308 {
00309   QList<QgsColorRampShader::ColorRampItem>::const_iterator colorRampIt = mColorRampItemList.constBegin();
00310   for ( ; colorRampIt != mColorRampItemList.constEnd(); ++colorRampIt )
00311   {
00312     symbolItems.push_back( qMakePair( colorRampIt->label, colorRampIt->color ) );
00313   }
00314 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines