QGIS API Documentation  3.13.0-Master (b73bd58cfb)
qgscontrastenhancement.cpp
Go to the documentation of this file.
1 /* **************************************************************************
2  qgscontrastenhancement.cpp - description
3  -------------------
4 begin : Mon Oct 22 2007
5 copyright : (C) 2007 by Peter J. Ersts
6 email : [email protected]
7 
8 This class contains code that was originally part of the larger QgsRasterLayer
9 class originally created circa 2004 by T.Sutton, Gary E.Sherman, Steve Halasz
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 #include "qgslogger.h"
22 
23 #include "qgscontrastenhancement.h"
28 #include "qgsrasterblock.h"
29 #include <QDomDocument>
30 #include <QDomElement>
31 
33  : mMinimumValue( minimumValuePossible( dataType ) )
34  , mMaximumValue( maximumValuePossible( dataType ) )
35  , mRasterDataType( dataType )
36  , mRasterDataTypeRange( mMaximumValue - mMinimumValue )
37  , mLookupTableOffset( mMinimumValue * -1 )
38 {
39  mContrastEnhancementFunction.reset( new QgsContrastEnhancementFunction( mRasterDataType, mMinimumValue, mMaximumValue ) );
40 
41  //If the data type is larger than 16-bit do not generate a lookup table
42  if ( mRasterDataTypeRange <= 65535.0 )
43  {
44  mLookupTable = new int[static_cast <int>( mRasterDataTypeRange + 1 )];
45  }
46 }
47 
49  : mEnhancementDirty( true )
50  , mMinimumValue( ce.mMinimumValue )
51  , mMaximumValue( ce.mMaximumValue )
52  , mRasterDataType( ce.mRasterDataType )
53  , mRasterDataTypeRange( ce.mRasterDataTypeRange )
54 {
55  mLookupTableOffset = minimumValuePossible( mRasterDataType ) * -1;
56 
57  // setContrastEnhancementAlgorithm sets also QgsContrastEnhancementFunction
58  setContrastEnhancementAlgorithm( ce.mContrastEnhancementAlgorithm, false );
59 
60  //If the data type is larger than 16-bit do not generate a lookup table
61  if ( mRasterDataTypeRange <= 65535.0 )
62  {
63  mLookupTable = new int[static_cast <int>( mRasterDataTypeRange + 1 )];
64  }
65 }
66 
68 {
69  delete [] mLookupTable;
70 }
71 
73 {
74  if ( mEnhancementDirty )
75  {
76  generateLookupTable();
77  }
78 
79  if ( mLookupTable && NoEnhancement != mContrastEnhancementAlgorithm )
80  {
81  double shiftedValue = value + mLookupTableOffset;
82  if ( shiftedValue >= 0 && shiftedValue < mRasterDataTypeRange + 1 )
83  return mLookupTable[static_cast <int>( shiftedValue )];
84  return 0;
85  }
86  else
87  {
88  // Even if the contrast enhancement algorithms is set to NoEnhancement
89  // The input values will still have to be scaled for all data types
90  // greater than 1 byte.
91  return mContrastEnhancementFunction->enhance( value );
92  }
93 }
94 
95 bool QgsContrastEnhancement::generateLookupTable()
96 {
97  mEnhancementDirty = false;
98 
99  if ( !mContrastEnhancementFunction )
100  return false;
101  if ( NoEnhancement == mContrastEnhancementAlgorithm )
102  return false;
103  if ( Qgis::Byte != mRasterDataType && Qgis::UInt16 != mRasterDataType && Qgis::Int16 != mRasterDataType )
104  return false;
105  if ( !mLookupTable )
106  return false;
107 
108  QgsDebugMsgLevel( QStringLiteral( "building lookup table" ), 4 );
109  QgsDebugMsgLevel( QStringLiteral( "***MinimumValue : %1" ).arg( mMinimumValue ), 4 );
110  QgsDebugMsgLevel( QStringLiteral( "***MaximumValue : %1" ).arg( mMaximumValue ), 4 );
111  QgsDebugMsgLevel( QStringLiteral( "***mLookupTableOffset : %1" ).arg( mLookupTableOffset ), 4 );
112  QgsDebugMsgLevel( QStringLiteral( "***mRasterDataTypeRange : %1" ).arg( mRasterDataTypeRange ), 4 );
113 
114  for ( int myIterator = 0; myIterator <= mRasterDataTypeRange; myIterator++ )
115  {
116  mLookupTable[myIterator] = mContrastEnhancementFunction->enhance( static_cast< double >( myIterator ) - mLookupTableOffset );
117  }
118 
119  return true;
120 }
121 
123 {
124  if ( mContrastEnhancementFunction )
125  {
126  return mContrastEnhancementFunction->isValueInDisplayableRange( value );
127  }
128 
129  return false;
130 }
131 
133 {
134  switch ( algorithm )
135  {
137  mContrastEnhancementFunction.reset( new QgsLinearMinMaxEnhancement( mRasterDataType, mMinimumValue, mMaximumValue ) );
138  break;
140  mContrastEnhancementFunction.reset( new QgsLinearMinMaxEnhancementWithClip( mRasterDataType, mMinimumValue, mMaximumValue ) );
141  break;
142  case ClipToMinimumMaximum :
143  mContrastEnhancementFunction.reset( new QgsClipToMinMaxEnhancement( mRasterDataType, mMinimumValue, mMaximumValue ) );
144  break;
146  //Do nothing
147  break;
148  default:
149  mContrastEnhancementFunction.reset( new QgsContrastEnhancementFunction( mRasterDataType, mMinimumValue, mMaximumValue ) );
150  break;
151  }
152 
153  mEnhancementDirty = true;
154  mContrastEnhancementAlgorithm = algorithm;
155 
156  if ( generateTable )
157  {
158  generateLookupTable();
159  }
160 }
161 
163 {
164  QgsDebugMsgLevel( QStringLiteral( "called" ), 4 );
165 
166  if ( function )
167  {
168  mContrastEnhancementFunction.reset( function );
169  mContrastEnhancementAlgorithm = UserDefinedEnhancement;
170  generateLookupTable();
171  }
172 }
173 
174 void QgsContrastEnhancement::setMaximumValue( double value, bool generateTable )
175 {
176  QgsDebugMsgLevel( "called value: " + QString::number( value ) + " generate lookup table: " + QString::number( static_cast< int >( generateTable ) ), 4 );
177 
178  if ( value > maximumValuePossible( mRasterDataType ) )
179  {
180  mMaximumValue = maximumValuePossible( mRasterDataType );
181  }
182  else
183  {
184  mMaximumValue = value;
185  }
186 
187  if ( mContrastEnhancementFunction )
188  {
189  mContrastEnhancementFunction->setMaximumValue( value );
190  }
191 
192  mEnhancementDirty = true;
193 
194  if ( generateTable )
195  {
196  generateLookupTable();
197  }
198 }
199 
200 void QgsContrastEnhancement::setMinimumValue( double value, bool generateTable )
201 {
202  QgsDebugMsgLevel( "called value: " + QString::number( value ) + " generate lookup table: " + QString::number( static_cast< int >( generateTable ) ), 4 );
203 
204  if ( value < minimumValuePossible( mRasterDataType ) )
205  {
206  mMinimumValue = minimumValuePossible( mRasterDataType );
207  }
208  else
209  {
210  mMinimumValue = value;
211  }
212 
213  if ( mContrastEnhancementFunction )
214  {
215  mContrastEnhancementFunction->setMinimumValue( value );
216  }
217 
218  mEnhancementDirty = true;
219 
220  if ( generateTable )
221  {
222  generateLookupTable();
223  }
224 }
225 
226 void QgsContrastEnhancement::writeXml( QDomDocument &doc, QDomElement &parentElem ) const
227 {
228  //minimum value
229  QDomElement minElem = doc.createElement( QStringLiteral( "minValue" ) );
230  QDomText minText = doc.createTextNode( QgsRasterBlock::printValue( mMinimumValue ) );
231  minElem.appendChild( minText );
232  parentElem.appendChild( minElem );
233 
234  //maximum value
235  QDomElement maxElem = doc.createElement( QStringLiteral( "maxValue" ) );
236  QDomText maxText = doc.createTextNode( QgsRasterBlock::printValue( mMaximumValue ) );
237  maxElem.appendChild( maxText );
238  parentElem.appendChild( maxElem );
239 
240  //algorithm
241  QDomElement algorithmElem = doc.createElement( QStringLiteral( "algorithm" ) );
242  QDomText algorithmText = doc.createTextNode( contrastEnhancementAlgorithmString( mContrastEnhancementAlgorithm ) );
243  algorithmElem.appendChild( algorithmText );
244  parentElem.appendChild( algorithmElem );
245 }
246 
247 void QgsContrastEnhancement::readXml( const QDomElement &elem )
248 {
249  QDomElement minValueElem = elem.firstChildElement( QStringLiteral( "minValue" ) );
250  if ( !minValueElem.isNull() )
251  {
252  mMinimumValue = minValueElem.text().toDouble();
253  }
254  QDomElement maxValueElem = elem.firstChildElement( QStringLiteral( "maxValue" ) );
255  if ( !maxValueElem.isNull() )
256  {
257  mMaximumValue = maxValueElem.text().toDouble();
258  }
259  QDomElement algorithmElem = elem.firstChildElement( QStringLiteral( "algorithm" ) );
260  if ( !algorithmElem.isNull() )
261  {
262  QString algorithmString = algorithmElem.text();
264  // old version ( < 19 Apr 2013) was using enum directly -> for backward compatibility
265  if ( algorithmString == QLatin1String( "0" ) )
266  {
267  algorithm = NoEnhancement;
268  }
269  else if ( algorithmString == QLatin1String( "1" ) )
270  {
271  algorithm = StretchToMinimumMaximum;
272  }
273  else if ( algorithmString == QLatin1String( "2" ) )
274  {
275  algorithm = StretchAndClipToMinimumMaximum;
276  }
277  else if ( algorithmString == QLatin1String( "3" ) )
278  {
279  algorithm = ClipToMinimumMaximum;
280  }
281  else if ( algorithmString == QLatin1String( "4" ) )
282  {
283  algorithm = UserDefinedEnhancement;
284  }
285  else
286  {
287  algorithm = contrastEnhancementAlgorithmFromString( algorithmString );
288  }
289 
290  setContrastEnhancementAlgorithm( algorithm );
291  }
292 }
293 
294 void QgsContrastEnhancement::toSld( QDomDocument &doc, QDomElement &element ) const
295 {
296  if ( doc.isNull() || element.isNull() )
297  return;
298 
299  QString algName;
300  switch ( contrastEnhancementAlgorithm() )
301  {
303  algName = QStringLiteral( "StretchToMinimumMaximum" );
304  break;
305  /* TODO: check if ClipToZero => StretchAndClipToMinimumMaximum
306  * because value outside min/max ar considered as NoData instead of 0 */
308  algName = QStringLiteral( "ClipToMinimumMaximum" );
309  break;
311  algName = QStringLiteral( "ClipToMinimumMaximum" );
312  break;
313  case NoEnhancement:
314  return;
317  QgsDebugMsg( QObject::tr( "No SLD1.0 conversion yet for stretch algorithm %1" ).arg( algName ) );
318  return;
319  }
320 
321  // Only <Normalize> is supported
322  // minValue and maxValue are that values as set depending on "Min /Max value settings"
323  // parameters
324  QDomElement normalizeElem = doc.createElement( QStringLiteral( "sld:Normalize" ) );
325  element.appendChild( normalizeElem );
326 
327  QDomElement vendorOptionAlgorithmElem = doc.createElement( QStringLiteral( "sld:VendorOption" ) );
328  vendorOptionAlgorithmElem.setAttribute( QStringLiteral( "name" ), QStringLiteral( "algorithm" ) );
329  vendorOptionAlgorithmElem.appendChild( doc.createTextNode( algName ) );
330  normalizeElem.appendChild( vendorOptionAlgorithmElem );
331 
332  QDomElement vendorOptionMinValueElem = doc.createElement( QStringLiteral( "sld:VendorOption" ) );
333  vendorOptionMinValueElem.setAttribute( QStringLiteral( "name" ), QStringLiteral( "minValue" ) );
334  vendorOptionMinValueElem.appendChild( doc.createTextNode( QString::number( minimumValue() ) ) );
335  normalizeElem.appendChild( vendorOptionMinValueElem );
336 
337  QDomElement vendorOptionMaxValueElem = doc.createElement( QStringLiteral( "sld:VendorOption" ) );
338  vendorOptionMaxValueElem.setAttribute( QStringLiteral( "name" ), QStringLiteral( "maxValue" ) );
339  vendorOptionMaxValueElem.appendChild( doc.createTextNode( QString::number( maximumValue() ) ) );
340  normalizeElem.appendChild( vendorOptionMaxValueElem );
341 }
342 
344 {
345  switch ( algorithm )
346  {
347  case NoEnhancement:
348  return QStringLiteral( "NoEnhancement" );
350  return QStringLiteral( "StretchToMinimumMaximum" );
352  return QStringLiteral( "StretchAndClipToMinimumMaximum" );
354  return QStringLiteral( "ClipToMinimumMaximum" );
356  return QStringLiteral( "UserDefinedEnhancement" );
357  }
358  return QStringLiteral( "NoEnhancement" );
359 }
360 
362 {
363  if ( contrastEnhancementString == QLatin1String( "StretchToMinimumMaximum" ) )
364  {
366  }
367  else if ( contrastEnhancementString == QLatin1String( "StretchAndClipToMinimumMaximum" ) )
368  {
370  }
371  else if ( contrastEnhancementString == QLatin1String( "ClipToMinimumMaximum" ) )
372  {
373  return ClipToMinimumMaximum;
374  }
375  else if ( contrastEnhancementString == QLatin1String( "UserDefinedEnhancement" ) )
376  {
377  return UserDefinedEnhancement;
378  }
379  else
380  {
381  return NoEnhancement;
382  }
383 }
double maximumValue() const
Returns the maximum value for the contrast enhancement range.
static QString printValue(double value)
Print double value with all necessary significant digits.
void setContrastEnhancementFunction(QgsContrastEnhancementFunction *function)
Allows the user to set their own custom contrast enhancement function.
static double minimumValuePossible(Qgis::DataType dataType)
Helper function that returns the minimum possible value for a GDAL data type.
void setMinimumValue(double value, bool generateTable=true)
Sets the minimum value for the contrast enhancement range.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
static double maximumValuePossible(Qgis::DataType dataType)
Helper function that returns the maximum possible value for a GDAL data type.
DataType
Raster data types.
Definition: qgis.h:101
double minimumValue() const
Returns the minimum value for the contrast enhancement range.
Sixteen bit signed integer (qint16)
Definition: qgis.h:106
void toSld(QDomDocument &doc, QDomElement &element) const
Write ContrastEnhancement tags following SLD v1.0 specs SLD1.0 is limited to the parameters listed in...
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
void setMaximumValue(double value, bool generateTable=true)
Sets the maximum value for the contrast enhancement range.
A linear enhanceContrast enhancement that first clips to min max and then enhanceContrastes linearly ...
bool isValueInDisplayableRange(double value)
Returns true if a pixel value is in displayable range, false if pixel is outside of range (i...
static QString contrastEnhancementAlgorithmString(ContrastEnhancementAlgorithm algorithm)
Returns a string to serialize ContrastEnhancementAlgorithm.
void readXml(const QDomElement &elem)
A contrast enhancement function is the base class for all raster contrast enhancements.
Sixteen bit unsigned integer (quint16)
Definition: qgis.h:105
A color enhancement function that performs a linear enhanceContrast between min and max...
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into allowing algorithms to be written in pure substantial changes are required in order to port existing x Processing algorithms for QGIS x The most significant changes are outlined not GeoAlgorithm For algorithms which operate on features one by consider subclassing the QgsProcessingFeatureBasedAlgorithm class This class allows much of the boilerplate code for looping over features from a vector layer to be bypassed and instead requires implementation of a processFeature method Ensure that your algorithm(or algorithm 's parent class) implements the new pure virtual createInstance(self) call
static ContrastEnhancementAlgorithm contrastEnhancementAlgorithmFromString(const QString &contrastEnhancementString)
Deserialize ContrastEnhancementAlgorithm.
ContrastEnhancementAlgorithm
This enumerator describes the types of contrast enhancement algorithms that can be used...
A raster contrast enhancement that will clip a value to the specified min/max range.
ContrastEnhancementAlgorithm contrastEnhancementAlgorithm() const
void writeXml(QDomDocument &doc, QDomElement &parentElem) const
Manipulates raster pixel values so that they enhanceContrast or clip into a specified numerical range...
QgsContrastEnhancement(Qgis::DataType datatype=Qgis::Byte)
void setContrastEnhancementAlgorithm(ContrastEnhancementAlgorithm algorithm, bool generateTable=true)
Sets the contrast enhancement algorithm.
Eight bit unsigned integer (quint8)
Definition: qgis.h:104
int enhanceContrast(double value)
Applies the contrast enhancement to a value.