1 /* **************************************************************************
2 qgscontrastenhancement.cpp - description
3 -------------------
4 begin : Mon Oct 22 2007
5 copyright : (C) 2007 by Peter J. Ersts
6 email : ersts@amnh.org
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"
24 #include "qgscontrastenhancementfunction.h"
25 #include "qgslinearminmaxenhancement.h"
26 #include "qgslinearminmaxenhancementwithclip.h"
27 #include "qgscliptominmaxenhancement.h"
28 #include "qgsrasterblock.h"
29 #include <QDomDocument>
30 #include <QDomElement>
31
QgsContrastEnhancement(Qgis::DataType dataType)32 QgsContrastEnhancement::QgsContrastEnhancement( Qgis::DataType dataType )
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
QgsContrastEnhancement(const QgsContrastEnhancement & ce)48 QgsContrastEnhancement::QgsContrastEnhancement( const QgsContrastEnhancement &ce )
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
~QgsContrastEnhancement()67 QgsContrastEnhancement::~QgsContrastEnhancement()
68 {
69 delete [] mLookupTable;
70 }
71
enhanceContrast(double value)72 int QgsContrastEnhancement::enhanceContrast( double value )
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
generateLookupTable()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::DataType::Byte != mRasterDataType && Qgis::DataType::UInt16 != mRasterDataType && Qgis::DataType::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
isValueInDisplayableRange(double value)122 bool QgsContrastEnhancement::isValueInDisplayableRange( double value )
123 {
124 if ( mContrastEnhancementFunction )
125 {
126 return mContrastEnhancementFunction->isValueInDisplayableRange( value );
127 }
128
129 return false;
130 }
131
setContrastEnhancementAlgorithm(ContrastEnhancementAlgorithm algorithm,bool generateTable)132 void QgsContrastEnhancement::setContrastEnhancementAlgorithm( ContrastEnhancementAlgorithm algorithm, bool generateTable )
133 {
134 switch ( algorithm )
135 {
136 case StretchToMinimumMaximum :
137 mContrastEnhancementFunction.reset( new QgsLinearMinMaxEnhancement( mRasterDataType, mMinimumValue, mMaximumValue ) );
138 break;
139 case StretchAndClipToMinimumMaximum :
140 mContrastEnhancementFunction.reset( new QgsLinearMinMaxEnhancementWithClip( mRasterDataType, mMinimumValue, mMaximumValue ) );
141 break;
142 case ClipToMinimumMaximum :
143 mContrastEnhancementFunction.reset( new QgsClipToMinMaxEnhancement( mRasterDataType, mMinimumValue, mMaximumValue ) );
144 break;
145 case UserDefinedEnhancement :
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
setContrastEnhancementFunction(QgsContrastEnhancementFunction * function)162 void QgsContrastEnhancement::setContrastEnhancementFunction( QgsContrastEnhancementFunction *function )
163 {
164 QgsDebugMsgLevel( QStringLiteral( "called" ), 4 );
165
166 if ( function )
167 {
168 mContrastEnhancementFunction.reset( function );
169 mContrastEnhancementAlgorithm = UserDefinedEnhancement;
170 generateLookupTable();
171 }
172 }
173
setMaximumValue(double value,bool generateTable)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
setMinimumValue(double value,bool generateTable)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
writeXml(QDomDocument & doc,QDomElement & parentElem) const226 void QgsContrastEnhancement::writeXml( QDomDocument &doc, QDomElement &parentElem ) const
227 {
228 //minimum value
229 QDomElement minElem = doc.createElement( QStringLiteral( "minValue" ) );
230 const 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 const 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 const QDomText algorithmText = doc.createTextNode( contrastEnhancementAlgorithmString( mContrastEnhancementAlgorithm ) );
243 algorithmElem.appendChild( algorithmText );
244 parentElem.appendChild( algorithmElem );
245 }
246
readXml(const QDomElement & elem)247 void QgsContrastEnhancement::readXml( const QDomElement &elem )
248 {
249 const QDomElement minValueElem = elem.firstChildElement( QStringLiteral( "minValue" ) );
250 if ( !minValueElem.isNull() )
251 {
252 mMinimumValue = minValueElem.text().toDouble();
253 }
254 const QDomElement maxValueElem = elem.firstChildElement( QStringLiteral( "maxValue" ) );
255 if ( !maxValueElem.isNull() )
256 {
257 mMaximumValue = maxValueElem.text().toDouble();
258 }
259 const QDomElement algorithmElem = elem.firstChildElement( QStringLiteral( "algorithm" ) );
260 if ( !algorithmElem.isNull() )
261 {
262 const QString algorithmString = algorithmElem.text();
263 ContrastEnhancementAlgorithm algorithm = NoEnhancement;
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
toSld(QDomDocument & doc,QDomElement & element) const294 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 {
302 case StretchToMinimumMaximum:
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 */
307 case StretchAndClipToMinimumMaximum:
308 algName = QStringLiteral( "ClipToMinimumMaximum" );
309 break;
310 case ClipToMinimumMaximum:
311 algName = QStringLiteral( "ClipToMinimumMaximum" );
312 break;
313 case NoEnhancement:
314 return;
315 case UserDefinedEnhancement:
316 algName = contrastEnhancementAlgorithmString( contrastEnhancementAlgorithm() );
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
contrastEnhancementAlgorithmString(ContrastEnhancementAlgorithm algorithm)343 QString QgsContrastEnhancement::contrastEnhancementAlgorithmString( ContrastEnhancementAlgorithm algorithm )
344 {
345 switch ( algorithm )
346 {
347 case NoEnhancement:
348 return QStringLiteral( "NoEnhancement" );
349 case StretchToMinimumMaximum:
350 return QStringLiteral( "StretchToMinimumMaximum" );
351 case StretchAndClipToMinimumMaximum:
352 return QStringLiteral( "StretchAndClipToMinimumMaximum" );
353 case ClipToMinimumMaximum:
354 return QStringLiteral( "ClipToMinimumMaximum" );
355 case UserDefinedEnhancement:
356 return QStringLiteral( "UserDefinedEnhancement" );
357 }
358 return QStringLiteral( "NoEnhancement" );
359 }
360
contrastEnhancementAlgorithmFromString(const QString & contrastEnhancementString)361 QgsContrastEnhancement::ContrastEnhancementAlgorithm QgsContrastEnhancement::contrastEnhancementAlgorithmFromString( const QString &contrastEnhancementString )
362 {
363 if ( contrastEnhancementString == QLatin1String( "StretchToMinimumMaximum" ) )
364 {
365 return StretchToMinimumMaximum;
366 }
367 else if ( contrastEnhancementString == QLatin1String( "StretchAndClipToMinimumMaximum" ) )
368 {
369 return StretchAndClipToMinimumMaximum;
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 }
384