Branch data Line data Source code
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 : : 32 : 0 : QgsContrastEnhancement::QgsContrastEnhancement( Qgis::DataType dataType ) 33 : 0 : : mMinimumValue( minimumValuePossible( dataType ) ) 34 : 0 : , mMaximumValue( maximumValuePossible( dataType ) ) 35 : 0 : , mRasterDataType( dataType ) 36 : 0 : , mRasterDataTypeRange( mMaximumValue - mMinimumValue ) 37 : 0 : , mLookupTableOffset( mMinimumValue * -1 ) 38 : : { 39 : 0 : 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 : 0 : if ( mRasterDataTypeRange <= 65535.0 ) 43 : : { 44 : 0 : mLookupTable = new int[static_cast <int>( mRasterDataTypeRange + 1 )]; 45 : 0 : } 46 : 0 : } 47 : : 48 : 0 : QgsContrastEnhancement::QgsContrastEnhancement( const QgsContrastEnhancement &ce ) 49 : 0 : : mEnhancementDirty( true ) 50 : 0 : , mMinimumValue( ce.mMinimumValue ) 51 : 0 : , mMaximumValue( ce.mMaximumValue ) 52 : 0 : , mRasterDataType( ce.mRasterDataType ) 53 : 0 : , mRasterDataTypeRange( ce.mRasterDataTypeRange ) 54 : : { 55 : 0 : mLookupTableOffset = minimumValuePossible( mRasterDataType ) * -1; 56 : : 57 : : // setContrastEnhancementAlgorithm sets also QgsContrastEnhancementFunction 58 : 0 : setContrastEnhancementAlgorithm( ce.mContrastEnhancementAlgorithm, false ); 59 : : 60 : : //If the data type is larger than 16-bit do not generate a lookup table 61 : 0 : if ( mRasterDataTypeRange <= 65535.0 ) 62 : : { 63 : 0 : mLookupTable = new int[static_cast <int>( mRasterDataTypeRange + 1 )]; 64 : 0 : } 65 : 0 : } 66 : : 67 : 0 : QgsContrastEnhancement::~QgsContrastEnhancement() 68 : : { 69 : 0 : delete [] mLookupTable; 70 : 0 : } 71 : : 72 : 0 : int QgsContrastEnhancement::enhanceContrast( double value ) 73 : : { 74 : 0 : if ( mEnhancementDirty ) 75 : : { 76 : 0 : generateLookupTable(); 77 : 0 : } 78 : : 79 : 0 : if ( mLookupTable && NoEnhancement != mContrastEnhancementAlgorithm ) 80 : : { 81 : 0 : double shiftedValue = value + mLookupTableOffset; 82 : 0 : if ( shiftedValue >= 0 && shiftedValue < mRasterDataTypeRange + 1 ) 83 : 0 : return mLookupTable[static_cast <int>( shiftedValue )]; 84 : 0 : 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 : 0 : return mContrastEnhancementFunction->enhance( value ); 92 : : } 93 : 0 : } 94 : : 95 : 0 : bool QgsContrastEnhancement::generateLookupTable() 96 : : { 97 : 0 : mEnhancementDirty = false; 98 : : 99 : 0 : if ( !mContrastEnhancementFunction ) 100 : 0 : return false; 101 : 0 : if ( NoEnhancement == mContrastEnhancementAlgorithm ) 102 : 0 : return false; 103 : 0 : if ( Qgis::Byte != mRasterDataType && Qgis::UInt16 != mRasterDataType && Qgis::Int16 != mRasterDataType ) 104 : 0 : return false; 105 : 0 : if ( !mLookupTable ) 106 : 0 : return false; 107 : : 108 : 0 : QgsDebugMsgLevel( QStringLiteral( "building lookup table" ), 4 ); 109 : 0 : QgsDebugMsgLevel( QStringLiteral( "***MinimumValue : %1" ).arg( mMinimumValue ), 4 ); 110 : 0 : QgsDebugMsgLevel( QStringLiteral( "***MaximumValue : %1" ).arg( mMaximumValue ), 4 ); 111 : 0 : QgsDebugMsgLevel( QStringLiteral( "***mLookupTableOffset : %1" ).arg( mLookupTableOffset ), 4 ); 112 : 0 : QgsDebugMsgLevel( QStringLiteral( "***mRasterDataTypeRange : %1" ).arg( mRasterDataTypeRange ), 4 ); 113 : : 114 : 0 : for ( int myIterator = 0; myIterator <= mRasterDataTypeRange; myIterator++ ) 115 : : { 116 : 0 : mLookupTable[myIterator] = mContrastEnhancementFunction->enhance( static_cast< double >( myIterator ) - mLookupTableOffset ); 117 : 0 : } 118 : : 119 : 0 : return true; 120 : 0 : } 121 : : 122 : 0 : bool QgsContrastEnhancement::isValueInDisplayableRange( double value ) 123 : : { 124 : 0 : if ( mContrastEnhancementFunction ) 125 : : { 126 : 0 : return mContrastEnhancementFunction->isValueInDisplayableRange( value ); 127 : : } 128 : : 129 : 0 : return false; 130 : 0 : } 131 : : 132 : 0 : void QgsContrastEnhancement::setContrastEnhancementAlgorithm( ContrastEnhancementAlgorithm algorithm, bool generateTable ) 133 : : { 134 : 0 : switch ( algorithm ) 135 : : { 136 : : case StretchToMinimumMaximum : 137 : 0 : mContrastEnhancementFunction.reset( new QgsLinearMinMaxEnhancement( mRasterDataType, mMinimumValue, mMaximumValue ) ); 138 : 0 : break; 139 : : case StretchAndClipToMinimumMaximum : 140 : 0 : mContrastEnhancementFunction.reset( new QgsLinearMinMaxEnhancementWithClip( mRasterDataType, mMinimumValue, mMaximumValue ) ); 141 : 0 : break; 142 : : case ClipToMinimumMaximum : 143 : 0 : mContrastEnhancementFunction.reset( new QgsClipToMinMaxEnhancement( mRasterDataType, mMinimumValue, mMaximumValue ) ); 144 : 0 : break; 145 : : case UserDefinedEnhancement : 146 : : //Do nothing 147 : 0 : break; 148 : : default: 149 : 0 : mContrastEnhancementFunction.reset( new QgsContrastEnhancementFunction( mRasterDataType, mMinimumValue, mMaximumValue ) ); 150 : 0 : break; 151 : : } 152 : : 153 : 0 : mEnhancementDirty = true; 154 : 0 : mContrastEnhancementAlgorithm = algorithm; 155 : : 156 : 0 : if ( generateTable ) 157 : : { 158 : 0 : generateLookupTable(); 159 : 0 : } 160 : 0 : } 161 : : 162 : 0 : void QgsContrastEnhancement::setContrastEnhancementFunction( QgsContrastEnhancementFunction *function ) 163 : : { 164 : 0 : QgsDebugMsgLevel( QStringLiteral( "called" ), 4 ); 165 : : 166 : 0 : if ( function ) 167 : : { 168 : 0 : mContrastEnhancementFunction.reset( function ); 169 : 0 : mContrastEnhancementAlgorithm = UserDefinedEnhancement; 170 : 0 : generateLookupTable(); 171 : 0 : } 172 : 0 : } 173 : : 174 : 0 : void QgsContrastEnhancement::setMaximumValue( double value, bool generateTable ) 175 : : { 176 : 0 : QgsDebugMsgLevel( "called value: " + QString::number( value ) + " generate lookup table: " + QString::number( static_cast< int >( generateTable ) ), 4 ); 177 : : 178 : 0 : if ( value > maximumValuePossible( mRasterDataType ) ) 179 : : { 180 : 0 : mMaximumValue = maximumValuePossible( mRasterDataType ); 181 : 0 : } 182 : : else 183 : : { 184 : 0 : mMaximumValue = value; 185 : : } 186 : : 187 : 0 : if ( mContrastEnhancementFunction ) 188 : : { 189 : 0 : mContrastEnhancementFunction->setMaximumValue( value ); 190 : 0 : } 191 : : 192 : 0 : mEnhancementDirty = true; 193 : : 194 : 0 : if ( generateTable ) 195 : : { 196 : 0 : generateLookupTable(); 197 : 0 : } 198 : 0 : } 199 : : 200 : 0 : void QgsContrastEnhancement::setMinimumValue( double value, bool generateTable ) 201 : : { 202 : 0 : QgsDebugMsgLevel( "called value: " + QString::number( value ) + " generate lookup table: " + QString::number( static_cast< int >( generateTable ) ), 4 ); 203 : : 204 : 0 : if ( value < minimumValuePossible( mRasterDataType ) ) 205 : : { 206 : 0 : mMinimumValue = minimumValuePossible( mRasterDataType ); 207 : 0 : } 208 : : else 209 : : { 210 : 0 : mMinimumValue = value; 211 : : } 212 : : 213 : 0 : if ( mContrastEnhancementFunction ) 214 : : { 215 : 0 : mContrastEnhancementFunction->setMinimumValue( value ); 216 : 0 : } 217 : : 218 : 0 : mEnhancementDirty = true; 219 : : 220 : 0 : if ( generateTable ) 221 : : { 222 : 0 : generateLookupTable(); 223 : 0 : } 224 : 0 : } 225 : : 226 : 0 : void QgsContrastEnhancement::writeXml( QDomDocument &doc, QDomElement &parentElem ) const 227 : : { 228 : : //minimum value 229 : 0 : QDomElement minElem = doc.createElement( QStringLiteral( "minValue" ) ); 230 : 0 : QDomText minText = doc.createTextNode( QgsRasterBlock::printValue( mMinimumValue ) ); 231 : 0 : minElem.appendChild( minText ); 232 : 0 : parentElem.appendChild( minElem ); 233 : : 234 : : //maximum value 235 : 0 : QDomElement maxElem = doc.createElement( QStringLiteral( "maxValue" ) ); 236 : 0 : QDomText maxText = doc.createTextNode( QgsRasterBlock::printValue( mMaximumValue ) ); 237 : 0 : maxElem.appendChild( maxText ); 238 : 0 : parentElem.appendChild( maxElem ); 239 : : 240 : : //algorithm 241 : 0 : QDomElement algorithmElem = doc.createElement( QStringLiteral( "algorithm" ) ); 242 : 0 : QDomText algorithmText = doc.createTextNode( contrastEnhancementAlgorithmString( mContrastEnhancementAlgorithm ) ); 243 : 0 : algorithmElem.appendChild( algorithmText ); 244 : 0 : parentElem.appendChild( algorithmElem ); 245 : 0 : } 246 : : 247 : 0 : void QgsContrastEnhancement::readXml( const QDomElement &elem ) 248 : : { 249 : 0 : QDomElement minValueElem = elem.firstChildElement( QStringLiteral( "minValue" ) ); 250 : 0 : if ( !minValueElem.isNull() ) 251 : : { 252 : 0 : mMinimumValue = minValueElem.text().toDouble(); 253 : 0 : } 254 : 0 : QDomElement maxValueElem = elem.firstChildElement( QStringLiteral( "maxValue" ) ); 255 : 0 : if ( !maxValueElem.isNull() ) 256 : : { 257 : 0 : mMaximumValue = maxValueElem.text().toDouble(); 258 : 0 : } 259 : 0 : QDomElement algorithmElem = elem.firstChildElement( QStringLiteral( "algorithm" ) ); 260 : 0 : if ( !algorithmElem.isNull() ) 261 : : { 262 : 0 : QString algorithmString = algorithmElem.text(); 263 : 0 : ContrastEnhancementAlgorithm algorithm = NoEnhancement; 264 : : // old version ( < 19 Apr 2013) was using enum directly -> for backward compatibility 265 : 0 : if ( algorithmString == QLatin1String( "0" ) ) 266 : : { 267 : 0 : algorithm = NoEnhancement; 268 : 0 : } 269 : 0 : else if ( algorithmString == QLatin1String( "1" ) ) 270 : : { 271 : 0 : algorithm = StretchToMinimumMaximum; 272 : 0 : } 273 : 0 : else if ( algorithmString == QLatin1String( "2" ) ) 274 : : { 275 : 0 : algorithm = StretchAndClipToMinimumMaximum; 276 : 0 : } 277 : 0 : else if ( algorithmString == QLatin1String( "3" ) ) 278 : : { 279 : 0 : algorithm = ClipToMinimumMaximum; 280 : 0 : } 281 : 0 : else if ( algorithmString == QLatin1String( "4" ) ) 282 : : { 283 : 0 : algorithm = UserDefinedEnhancement; 284 : 0 : } 285 : : else 286 : : { 287 : 0 : algorithm = contrastEnhancementAlgorithmFromString( algorithmString ); 288 : : } 289 : : 290 : 0 : setContrastEnhancementAlgorithm( algorithm ); 291 : 0 : } 292 : 0 : } 293 : : 294 : 0 : void QgsContrastEnhancement::toSld( QDomDocument &doc, QDomElement &element ) const 295 : : { 296 : 0 : if ( doc.isNull() || element.isNull() ) 297 : 0 : return; 298 : : 299 : 0 : QString algName; 300 : 0 : switch ( contrastEnhancementAlgorithm() ) 301 : : { 302 : : case StretchToMinimumMaximum: 303 : 0 : algName = QStringLiteral( "StretchToMinimumMaximum" ); 304 : 0 : break; 305 : : /* TODO: check if ClipToZero => StretchAndClipToMinimumMaximum 306 : : * because value outside min/max ar considered as NoData instead of 0 */ 307 : : case StretchAndClipToMinimumMaximum: 308 : 0 : algName = QStringLiteral( "ClipToMinimumMaximum" ); 309 : 0 : break; 310 : : case ClipToMinimumMaximum: 311 : 0 : algName = QStringLiteral( "ClipToMinimumMaximum" ); 312 : 0 : break; 313 : : case NoEnhancement: 314 : 0 : return; 315 : : case UserDefinedEnhancement: 316 : 0 : algName = contrastEnhancementAlgorithmString( contrastEnhancementAlgorithm() ); 317 : 0 : QgsDebugMsg( QObject::tr( "No SLD1.0 conversion yet for stretch algorithm %1" ).arg( algName ) ); 318 : 0 : 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 : 0 : QDomElement normalizeElem = doc.createElement( QStringLiteral( "sld:Normalize" ) ); 325 : 0 : element.appendChild( normalizeElem ); 326 : : 327 : 0 : QDomElement vendorOptionAlgorithmElem = doc.createElement( QStringLiteral( "sld:VendorOption" ) ); 328 : 0 : vendorOptionAlgorithmElem.setAttribute( QStringLiteral( "name" ), QStringLiteral( "algorithm" ) ); 329 : 0 : vendorOptionAlgorithmElem.appendChild( doc.createTextNode( algName ) ); 330 : 0 : normalizeElem.appendChild( vendorOptionAlgorithmElem ); 331 : : 332 : 0 : QDomElement vendorOptionMinValueElem = doc.createElement( QStringLiteral( "sld:VendorOption" ) ); 333 : 0 : vendorOptionMinValueElem.setAttribute( QStringLiteral( "name" ), QStringLiteral( "minValue" ) ); 334 : 0 : vendorOptionMinValueElem.appendChild( doc.createTextNode( QString::number( minimumValue() ) ) ); 335 : 0 : normalizeElem.appendChild( vendorOptionMinValueElem ); 336 : : 337 : 0 : QDomElement vendorOptionMaxValueElem = doc.createElement( QStringLiteral( "sld:VendorOption" ) ); 338 : 0 : vendorOptionMaxValueElem.setAttribute( QStringLiteral( "name" ), QStringLiteral( "maxValue" ) ); 339 : 0 : vendorOptionMaxValueElem.appendChild( doc.createTextNode( QString::number( maximumValue() ) ) ); 340 : 0 : normalizeElem.appendChild( vendorOptionMaxValueElem ); 341 : 0 : } 342 : : 343 : 0 : QString QgsContrastEnhancement::contrastEnhancementAlgorithmString( ContrastEnhancementAlgorithm algorithm ) 344 : : { 345 : 0 : switch ( algorithm ) 346 : : { 347 : : case NoEnhancement: 348 : 0 : return QStringLiteral( "NoEnhancement" ); 349 : : case StretchToMinimumMaximum: 350 : 0 : return QStringLiteral( "StretchToMinimumMaximum" ); 351 : : case StretchAndClipToMinimumMaximum: 352 : 0 : return QStringLiteral( "StretchAndClipToMinimumMaximum" ); 353 : : case ClipToMinimumMaximum: 354 : 0 : return QStringLiteral( "ClipToMinimumMaximum" ); 355 : : case UserDefinedEnhancement: 356 : 0 : return QStringLiteral( "UserDefinedEnhancement" ); 357 : : } 358 : 0 : return QStringLiteral( "NoEnhancement" ); 359 : 0 : } 360 : : 361 : 0 : QgsContrastEnhancement::ContrastEnhancementAlgorithm QgsContrastEnhancement::contrastEnhancementAlgorithmFromString( const QString &contrastEnhancementString ) 362 : : { 363 : 0 : if ( contrastEnhancementString == QLatin1String( "StretchToMinimumMaximum" ) ) 364 : : { 365 : 0 : return StretchToMinimumMaximum; 366 : : } 367 : 0 : else if ( contrastEnhancementString == QLatin1String( "StretchAndClipToMinimumMaximum" ) ) 368 : : { 369 : 0 : return StretchAndClipToMinimumMaximum; 370 : : } 371 : 0 : else if ( contrastEnhancementString == QLatin1String( "ClipToMinimumMaximum" ) ) 372 : : { 373 : 0 : return ClipToMinimumMaximum; 374 : : } 375 : 0 : else if ( contrastEnhancementString == QLatin1String( "UserDefinedEnhancement" ) ) 376 : : { 377 : 0 : return UserDefinedEnhancement; 378 : : } 379 : : else 380 : : { 381 : 0 : return NoEnhancement; 382 : : } 383 : 0 : }