QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
qgscontrastenhancement.cpp
Go to the documentation of this file.
1/* **************************************************************************
2 qgscontrastenhancement.cpp - description
3 -------------------
4begin : Mon Oct 22 2007
5copyright : (C) 2007 by Peter J. Ersts
7
8This class contains code that was originally part of the larger QgsRasterLayer
9class 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
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 const 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
95bool QgsContrastEnhancement::generateLookupTable()
96{
97 mEnhancementDirty = false;
98
99 if ( !mContrastEnhancementFunction )
100 return false;
101 if ( NoEnhancement == mContrastEnhancementAlgorithm )
102 return false;
103
104 switch ( mRasterDataType )
105 {
110 break;
111
123 return false;
124 }
125
126 if ( !mLookupTable )
127 return false;
128
129 QgsDebugMsgLevel( QStringLiteral( "building lookup table" ), 4 );
130 QgsDebugMsgLevel( QStringLiteral( "***MinimumValue : %1" ).arg( mMinimumValue ), 4 );
131 QgsDebugMsgLevel( QStringLiteral( "***MaximumValue : %1" ).arg( mMaximumValue ), 4 );
132 QgsDebugMsgLevel( QStringLiteral( "***mLookupTableOffset : %1" ).arg( mLookupTableOffset ), 4 );
133 QgsDebugMsgLevel( QStringLiteral( "***mRasterDataTypeRange : %1" ).arg( mRasterDataTypeRange ), 4 );
134
135 for ( int myIterator = 0; myIterator <= mRasterDataTypeRange; myIterator++ )
136 {
137 mLookupTable[myIterator] = mContrastEnhancementFunction->enhance( static_cast< double >( myIterator ) - mLookupTableOffset );
138 }
139
140 return true;
141}
142
144{
145 if ( mContrastEnhancementFunction )
146 {
147 return mContrastEnhancementFunction->isValueInDisplayableRange( value );
148 }
149
150 return false;
151}
152
154{
155 switch ( algorithm )
156 {
158 mContrastEnhancementFunction.reset( new QgsLinearMinMaxEnhancement( mRasterDataType, mMinimumValue, mMaximumValue ) );
159 break;
161 mContrastEnhancementFunction.reset( new QgsLinearMinMaxEnhancementWithClip( mRasterDataType, mMinimumValue, mMaximumValue ) );
162 break;
164 mContrastEnhancementFunction.reset( new QgsClipToMinMaxEnhancement( mRasterDataType, mMinimumValue, mMaximumValue ) );
165 break;
167 //Do nothing
168 break;
169 default:
170 mContrastEnhancementFunction.reset( new QgsContrastEnhancementFunction( mRasterDataType, mMinimumValue, mMaximumValue ) );
171 break;
172 }
173
174 mEnhancementDirty = true;
175 mContrastEnhancementAlgorithm = algorithm;
176
177 if ( generateTable )
178 {
179 generateLookupTable();
180 }
181}
182
184{
185 QgsDebugMsgLevel( QStringLiteral( "called" ), 4 );
186
187 if ( function )
188 {
189 mContrastEnhancementFunction.reset( function );
190 mContrastEnhancementAlgorithm = UserDefinedEnhancement;
191 generateLookupTable();
192 }
193}
194
195void QgsContrastEnhancement::setMaximumValue( double value, bool generateTable )
196{
197 QgsDebugMsgLevel( "called value: " + QString::number( value ) + " generate lookup table: " + QString::number( static_cast< int >( generateTable ) ), 4 );
198
199 if ( value > maximumValuePossible( mRasterDataType ) )
200 {
201 mMaximumValue = maximumValuePossible( mRasterDataType );
202 }
203 else
204 {
205 mMaximumValue = value;
206 }
207
208 if ( mContrastEnhancementFunction )
209 {
210 mContrastEnhancementFunction->setMaximumValue( value );
211 }
212
213 mEnhancementDirty = true;
214
215 if ( generateTable )
216 {
217 generateLookupTable();
218 }
219}
220
221void QgsContrastEnhancement::setMinimumValue( double value, bool generateTable )
222{
223 QgsDebugMsgLevel( "called value: " + QString::number( value ) + " generate lookup table: " + QString::number( static_cast< int >( generateTable ) ), 4 );
224
225 if ( value < minimumValuePossible( mRasterDataType ) )
226 {
227 mMinimumValue = minimumValuePossible( mRasterDataType );
228 }
229 else
230 {
231 mMinimumValue = value;
232 }
233
234 if ( mContrastEnhancementFunction )
235 {
236 mContrastEnhancementFunction->setMinimumValue( value );
237 }
238
239 mEnhancementDirty = true;
240
241 if ( generateTable )
242 {
243 generateLookupTable();
244 }
245}
246
247void QgsContrastEnhancement::writeXml( QDomDocument &doc, QDomElement &parentElem ) const
248{
249 //minimum value
250 QDomElement minElem = doc.createElement( QStringLiteral( "minValue" ) );
251 const QDomText minText = doc.createTextNode( QgsRasterBlock::printValue( mMinimumValue ) );
252 minElem.appendChild( minText );
253 parentElem.appendChild( minElem );
254
255 //maximum value
256 QDomElement maxElem = doc.createElement( QStringLiteral( "maxValue" ) );
257 const QDomText maxText = doc.createTextNode( QgsRasterBlock::printValue( mMaximumValue ) );
258 maxElem.appendChild( maxText );
259 parentElem.appendChild( maxElem );
260
261 //algorithm
262 QDomElement algorithmElem = doc.createElement( QStringLiteral( "algorithm" ) );
263 const QDomText algorithmText = doc.createTextNode( contrastEnhancementAlgorithmString( mContrastEnhancementAlgorithm ) );
264 algorithmElem.appendChild( algorithmText );
265 parentElem.appendChild( algorithmElem );
266}
267
268void QgsContrastEnhancement::readXml( const QDomElement &elem )
269{
270 const QDomElement minValueElem = elem.firstChildElement( QStringLiteral( "minValue" ) );
271 if ( !minValueElem.isNull() )
272 {
273 mMinimumValue = minValueElem.text().toDouble();
274 }
275 const QDomElement maxValueElem = elem.firstChildElement( QStringLiteral( "maxValue" ) );
276 if ( !maxValueElem.isNull() )
277 {
278 mMaximumValue = maxValueElem.text().toDouble();
279 }
280 const QDomElement algorithmElem = elem.firstChildElement( QStringLiteral( "algorithm" ) );
281 if ( !algorithmElem.isNull() )
282 {
283 const QString algorithmString = algorithmElem.text();
285 // old version ( < 19 Apr 2013) was using enum directly -> for backward compatibility
286 if ( algorithmString == QLatin1String( "0" ) )
287 {
289 }
290 else if ( algorithmString == QLatin1String( "1" ) )
291 {
293 }
294 else if ( algorithmString == QLatin1String( "2" ) )
295 {
297 }
298 else if ( algorithmString == QLatin1String( "3" ) )
299 {
301 }
302 else if ( algorithmString == QLatin1String( "4" ) )
303 {
305 }
306 else
307 {
309 }
310
312 }
313}
314
315void QgsContrastEnhancement::toSld( QDomDocument &doc, QDomElement &element ) const
316{
317 if ( doc.isNull() || element.isNull() )
318 return;
319
320 QString algName;
322 {
324 algName = QStringLiteral( "StretchToMinimumMaximum" );
325 break;
326 /* TODO: check if ClipToZero => StretchAndClipToMinimumMaximum
327 * because value outside min/max ar considered as NoData instead of 0 */
329 algName = QStringLiteral( "ClipToMinimumMaximum" );
330 break;
332 algName = QStringLiteral( "ClipToMinimumMaximum" );
333 break;
334 case NoEnhancement:
335 return;
338 QgsDebugError( QStringLiteral( "No SLD1.0 conversion yet for stretch algorithm %1" ).arg( algName ) );
339 return;
340 }
341
342 // Only <Normalize> is supported
343 // minValue and maxValue are that values as set depending on "Min /Max value settings"
344 // parameters
345 QDomElement normalizeElem = doc.createElement( QStringLiteral( "sld:Normalize" ) );
346 element.appendChild( normalizeElem );
347
348 QDomElement vendorOptionAlgorithmElem = doc.createElement( QStringLiteral( "sld:VendorOption" ) );
349 vendorOptionAlgorithmElem.setAttribute( QStringLiteral( "name" ), QStringLiteral( "algorithm" ) );
350 vendorOptionAlgorithmElem.appendChild( doc.createTextNode( algName ) );
351 normalizeElem.appendChild( vendorOptionAlgorithmElem );
352
353 QDomElement vendorOptionMinValueElem = doc.createElement( QStringLiteral( "sld:VendorOption" ) );
354 vendorOptionMinValueElem.setAttribute( QStringLiteral( "name" ), QStringLiteral( "minValue" ) );
355 vendorOptionMinValueElem.appendChild( doc.createTextNode( QString::number( minimumValue() ) ) );
356 normalizeElem.appendChild( vendorOptionMinValueElem );
357
358 QDomElement vendorOptionMaxValueElem = doc.createElement( QStringLiteral( "sld:VendorOption" ) );
359 vendorOptionMaxValueElem.setAttribute( QStringLiteral( "name" ), QStringLiteral( "maxValue" ) );
360 vendorOptionMaxValueElem.appendChild( doc.createTextNode( QString::number( maximumValue() ) ) );
361 normalizeElem.appendChild( vendorOptionMaxValueElem );
362}
363
365{
366 switch ( algorithm )
367 {
368 case NoEnhancement:
369 return QStringLiteral( "NoEnhancement" );
371 return QStringLiteral( "StretchToMinimumMaximum" );
373 return QStringLiteral( "StretchAndClipToMinimumMaximum" );
375 return QStringLiteral( "ClipToMinimumMaximum" );
377 return QStringLiteral( "UserDefinedEnhancement" );
378 }
379 return QStringLiteral( "NoEnhancement" );
380}
381
383{
384 if ( contrastEnhancementString == QLatin1String( "StretchToMinimumMaximum" ) )
385 {
387 }
388 else if ( contrastEnhancementString == QLatin1String( "StretchAndClipToMinimumMaximum" ) )
389 {
391 }
392 else if ( contrastEnhancementString == QLatin1String( "ClipToMinimumMaximum" ) )
393 {
395 }
396 else if ( contrastEnhancementString == QLatin1String( "UserDefinedEnhancement" ) )
397 {
399 }
400 else
401 {
402 return NoEnhancement;
403 }
404}
DataType
Raster data types.
Definition: qgis.h:269
@ CInt32
Complex Int32.
@ Float32
Thirty two bit floating point (float)
@ CFloat64
Complex Float64.
@ Int16
Sixteen bit signed integer (qint16)
@ ARGB32_Premultiplied
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32_Premultiplied.
@ Int8
Eight bit signed integer (qint8) (added in QGIS 3.30)
@ UInt16
Sixteen bit unsigned integer (quint16)
@ Byte
Eight bit unsigned integer (quint8)
@ UnknownDataType
Unknown or unspecified type.
@ ARGB32
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32.
@ Int32
Thirty two bit signed integer (qint32)
@ Float64
Sixty four bit floating point (double)
@ CFloat32
Complex Float32.
@ CInt16
Complex Int16.
@ UInt32
Thirty two bit unsigned integer (quint32)
A raster contrast enhancement that will clip a value to the specified min/max range.
A contrast enhancement function is the base class for all raster contrast enhancements.
Manipulates raster or point cloud pixel values so that they enhanceContrast or clip into a specified ...
ContrastEnhancementAlgorithm
This enumerator describes the types of contrast enhancement algorithms that can be used.
@ StretchToMinimumMaximum
Linear histogram.
@ NoEnhancement
Default color scaling algorithm, no scaling is applied.
QgsContrastEnhancement(Qgis::DataType datatype=Qgis::DataType::Byte)
Constructor for QgsContrastEnhancement, for the specified data type.
void setMinimumValue(double value, bool generateTable=true)
Sets the minimum value for the contrast enhancement range.
static QString contrastEnhancementAlgorithmString(ContrastEnhancementAlgorithm algorithm)
Returns a string to serialize ContrastEnhancementAlgorithm.
static ContrastEnhancementAlgorithm contrastEnhancementAlgorithmFromString(const QString &contrastEnhancementString)
Deserialize ContrastEnhancementAlgorithm.
static double maximumValuePossible(Qgis::DataType dataType)
Helper function that returns the maximum possible value for a data type.
void setContrastEnhancementAlgorithm(ContrastEnhancementAlgorithm algorithm, bool generateTable=true)
Sets the contrast enhancement algorithm.
bool isValueInDisplayableRange(double value)
Returns true if a pixel value is in displayable range, false if pixel is outside of range (i....
int enhanceContrast(double value)
Applies the contrast enhancement to a value.
void writeXml(QDomDocument &doc, QDomElement &parentElem) const
void readXml(const QDomElement &elem)
double minimumValue() const
Returns the minimum value for the contrast enhancement range.
static double minimumValuePossible(Qgis::DataType dataType)
Helper function that returns the minimum possible value for a data type.
void setContrastEnhancementFunction(QgsContrastEnhancementFunction *function)
Allows the user to set their own custom contrast enhancement function.
void toSld(QDomDocument &doc, QDomElement &element) const
Write ContrastEnhancement tags following SLD v1.0 specs SLD1.0 is limited to the parameters listed in...
ContrastEnhancementAlgorithm contrastEnhancementAlgorithm() const
void setMaximumValue(double value, bool generateTable=true)
Sets the maximum value for the contrast enhancement range.
double maximumValue() const
Returns the maximum value for the contrast enhancement range.
A linear enhanceContrast enhancement that first clips to min max and then enhanceContrastes linearly ...
A color enhancement function that performs a linear enhanceContrast between min and max.
static QString printValue(double value)
Print double value with all necessary significant digits.
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
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugError(str)
Definition: qgslogger.h:38