diff --git a/images/images.qrc b/images/images.qrc index 040731dccaa..42c46d02989 100644 --- a/images/images.qrc +++ b/images/images.qrc @@ -79,6 +79,7 @@ themes/default/mActionFormAnnotation.png themes/default/mActionFreezeLabels.png themes/default/mActionFromSelectedFeature.png + themes/default/mActionFullCumulativeCutStretch.png themes/default/mActionFullHistogramStretch.png themes/default/mActionGroupItems.png themes/default/mActionHelpAbout.png @@ -91,6 +92,7 @@ themes/default/mActionInvertSelection.png themes/default/mActionLabeling.png themes/default/mActionLabel.png + themes/default/mActionLocalCumulativeCutStretch.png themes/default/mActionLocalHistogramStretch.png themes/default/mActionLowerItems.png themes/default/mActionMapTips.png diff --git a/images/themes/default/mActionFullCumulativeCutStretch.png b/images/themes/default/mActionFullCumulativeCutStretch.png new file mode 100644 index 00000000000..7f98110f5b1 Binary files /dev/null and b/images/themes/default/mActionFullCumulativeCutStretch.png differ diff --git a/images/themes/default/mActionLocalCumulativeCutStretch.png b/images/themes/default/mActionLocalCumulativeCutStretch.png new file mode 100644 index 00000000000..c7f2e61d15b Binary files /dev/null and b/images/themes/default/mActionLocalCumulativeCutStretch.png differ diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index 0521ac99e10..8aac076b405 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -963,6 +963,8 @@ void QgisApp::createActions() // Raster toolbar items connect( mActionLocalHistogramStretch, SIGNAL( triggered() ), this, SLOT( localHistogramStretch() ) ); connect( mActionFullHistogramStretch, SIGNAL( triggered() ), this, SLOT( fullHistogramStretch() ) ); + connect( mActionLocalCumulativeCutStretch, SIGNAL( triggered() ), this, SLOT( localCumulativeCutStretch() ) ); + connect( mActionFullCumulativeCutStretch, SIGNAL( triggered() ), this, SLOT( fullCumulativeCutStretch() ) ); // Help Menu Items @@ -5461,14 +5463,29 @@ void QgisApp::options() void QgisApp::fullHistogramStretch() { - histogramStretch( false ); + histogramStretch( false, QgsRasterLayer::ContrastEnhancementMinMax ); } -void QgisApp::histogramStretch( bool visibleAreaOnly ) +void QgisApp::localHistogramStretch() { - QgsMapLayer * layer = mMapLegend->currentLayer(); + histogramStretch( true, QgsRasterLayer::ContrastEnhancementMinMax ); +} - if ( !layer ) +void QgisApp::fullCumulativeCutStretch() +{ + histogramStretch( false, QgsRasterLayer::ContrastEnhancementCumulativeCut ); +} + +void QgisApp::localCumulativeCutStretch() +{ + histogramStretch( true, QgsRasterLayer::ContrastEnhancementCumulativeCut ); +} + +void QgisApp::histogramStretch( bool visibleAreaOnly, QgsRasterLayer::ContrastEnhancementLimits theLimits ) +{ + QgsMapLayer * myLayer = mMapLegend->currentLayer(); + + if ( !myLayer ) { QMessageBox::information( this, tr( "No Layer Selected" ), @@ -5476,8 +5493,8 @@ void QgisApp::histogramStretch( bool visibleAreaOnly ) return; } - QgsRasterLayer* rlayer = qobject_cast( layer ); - if ( !rlayer ) + QgsRasterLayer* myRasterLayer = qobject_cast( myLayer ); + if ( !myRasterLayer ) { QMessageBox::information( this, tr( "No Raster Layer Selected" ), @@ -5485,118 +5502,14 @@ void QgisApp::histogramStretch( bool visibleAreaOnly ) return; } - QgsRasterDataProvider* provider = rlayer->dataProvider(); - if ( !provider ) - { - return; - } + QgsRectangle myRectangle; + if ( visibleAreaOnly ) myRectangle = mMapCanvas->mapRenderer()->outputExtentToLayerExtent( myRasterLayer, mMapCanvas->extent() ); - //get renderer - QgsRasterRenderer* renderer = rlayer->renderer(); - if ( !renderer ) - { - return; - } - - //singleband gray <-> multiband color - if ( renderer->type() == "singlebandgray" ) - { - QgsSingleBandGrayRenderer* grayRenderer = static_cast( renderer ); - if ( !grayRenderer ) - { - return; - } - - //create new contrast enhancements - int grayBand = grayRenderer->grayBand(); - if ( grayBand == -1 ) - { - return; - } - - QgsContrastEnhancement* e = rasterContrastEnhancement( rlayer, grayBand, visibleAreaOnly ); - if ( !e ) - { - return; - } - grayRenderer->setContrastEnhancement( e ); - } - else if ( renderer->type() == "multibandcolor" ) - { - QgsMultiBandColorRenderer* colorRenderer = static_cast( renderer ); - if ( !colorRenderer ) - { - return; - } - - QgsContrastEnhancement* redEnhancement = rasterContrastEnhancement( rlayer, colorRenderer->redBand(), visibleAreaOnly ); - if ( redEnhancement ) - { - colorRenderer->setRedContrastEnhancement( redEnhancement ); - } - QgsContrastEnhancement* greenEnhancement = rasterContrastEnhancement( rlayer, colorRenderer->greenBand(), visibleAreaOnly ); - if ( greenEnhancement ) - { - colorRenderer->setGreenContrastEnhancement( greenEnhancement ); - } - QgsContrastEnhancement* blueEnhancement = rasterContrastEnhancement( rlayer, colorRenderer->blueBand(), visibleAreaOnly ); - if ( blueEnhancement ) - { - colorRenderer->setBlueContrastEnhancement( blueEnhancement ); - } - } - else - { - return; - } + myRasterLayer->setContrastEnhancementAlgorithm( QgsContrastEnhancement::StretchToMinimumMaximum, theLimits, myRectangle ); mMapCanvas->refresh(); } -QgsContrastEnhancement* QgisApp::rasterContrastEnhancement( QgsRasterLayer* rlayer, int band, - bool visibleAreaOnly ) const -{ - if ( !rlayer || band == -1 ) - { - return 0; - } - - QgsRasterDataProvider* provider = rlayer->dataProvider(); - if ( !provider ) - { - return 0; - } - - QgsContrastEnhancement* e = new QgsContrastEnhancement(( QgsContrastEnhancement::QgsRasterDataType )( - provider->dataType( band ) ) ); - double minValue = 0; - double maxValue = 0; - - if ( visibleAreaOnly ) - { - double minMax[2]; - rlayer->computeMinimumMaximumFromLastExtent( band, minMax ); - minValue = minMax[0]; - maxValue = minMax[1]; - } - else - { - QgsRasterBandStats rasterBandStats = rlayer->dataProvider()->bandStatistics( band ); - minValue = rasterBandStats.minimumValue; - maxValue = rasterBandStats.maximumValue; - } - - e->setMinimumValue( minValue ); - e->setMaximumValue( maxValue ); - e->setContrastEnhancementAlgorithm( QgsContrastEnhancement::StretchToMinimumMaximum ); - return e; -} - -void QgisApp::localHistogramStretch() -{ - histogramStretch( true ); -} - void QgisApp::helpContents() { openURL( "index.html" ); diff --git a/src/app/qgisapp.h b/src/app/qgisapp.h index 06f4c032d01..42480ee9d61 100644 --- a/src/app/qgisapp.h +++ b/src/app/qgisapp.h @@ -55,7 +55,6 @@ class QgsPalLabeling; class QgsPoint; class QgsProviderRegistry; class QgsPythonUtils; -class QgsRasterLayer; class QgsRectangle; class QgsUndoWidget; class QgsVectorLayer; @@ -87,6 +86,7 @@ class QgsTileScaleWidget; #include "qgsconfig.h" #include "qgsfeature.h" #include "qgspoint.h" +#include "qgsrasterlayer.h" #include "qgssnappingdialog.h" #include "ui_qgisapp.h" @@ -548,6 +548,10 @@ class QgisApp : public QMainWindow, private Ui::MainWindow * Valid for non wms raster layers only. * @note Added in QGIS 1.7 */ void fullHistogramStretch(); + /** Perform a local cumulative cut stretch */ + void localCumulativeCutStretch(); + /** Perform a full extent cumulative cut stretch */ + void fullCumulativeCutStretch(); //! plugin manager void showPluginManager(); //! load python support if possible @@ -992,10 +996,7 @@ class QgisApp : public QMainWindow, private Ui::MainWindow void createDecorations(); /**Do histogram stretch for singleband gray / multiband color rasters*/ - void histogramStretch( bool visibleAreaOnly = false ); - /**Create raster enhancement object for a raster band*/ - QgsContrastEnhancement* rasterContrastEnhancement( QgsRasterLayer* rlayer, int band, - bool visibleAreaOnly = false ) const; + void histogramStretch( bool visibleAreaOnly = false, QgsRasterLayer::ContrastEnhancementLimits theLimits = QgsRasterLayer::ContrastEnhancementMinMax ); // actions for menus and toolbars ----------------- diff --git a/src/core/qgsrasterdataprovider.cpp b/src/core/qgsrasterdataprovider.cpp index a4b60554c0b..9efeac2c088 100644 --- a/src/core/qgsrasterdataprovider.cpp +++ b/src/core/qgsrasterdataprovider.cpp @@ -435,7 +435,15 @@ void QgsRasterDataProvider::initStatistics( QgsRasterBandStats &theStatistics, theStatistics.bandNumber = theBandNo; theStatistics.statsGathered = theStats; - QgsRectangle myExtent = theExtent.isEmpty() ? extent() : theExtent; + QgsRectangle myExtent; + if ( theExtent.isEmpty() ) + { + myExtent = extent(); + } + else + { + myExtent = extent().intersect( &theExtent ); + } theStatistics.extent = myExtent; if ( theSampleSize > 0 ) @@ -674,7 +682,15 @@ void QgsRasterDataProvider::initHistogram( QgsRasterHistogram &theHistogram, } } - QgsRectangle myExtent = theExtent.isEmpty() ? extent() : theExtent; + QgsRectangle myExtent; + if ( theExtent.isEmpty() ) + { + myExtent = extent(); + } + else + { + myExtent = extent().intersect( &theExtent ); + } theHistogram.extent = myExtent; if ( theSampleSize > 0 ) diff --git a/src/core/raster/qgsrasterlayer.cpp b/src/core/raster/qgsrasterlayer.cpp index a503c560f9b..75d3af40ce5 100644 --- a/src/core/raster/qgsrasterlayer.cpp +++ b/src/core/raster/qgsrasterlayer.cpp @@ -96,6 +96,10 @@ typedef bool isvalidrasterfilename_t( QString const & theFileNameQString, QStrin // doubles can take for the current system. (Yes, 20 was arbitrary.) #define TINY_VALUE std::numeric_limits::epsilon() * 20 +const double QgsRasterLayer::CUMULATIVE_CUT_LOWER = 0.02; +const double QgsRasterLayer::CUMULATIVE_CUT_UPPER = 0.98; +const double QgsRasterLayer::SAMPLE_SIZE = 250000; + QgsRasterLayer::QgsRasterLayer() : QgsMapLayer( RasterLayer ) , QSTRING_NOT_SET( "Not Set" ) @@ -1845,71 +1849,91 @@ void QgsRasterLayer::setColorShadingAlgorithm( QString ) void QgsRasterLayer::setContrastEnhancementAlgorithm( QgsContrastEnhancement::ContrastEnhancementAlgorithm theAlgorithm, bool theGenerateLookupTableFlag ) { + setContrastEnhancementAlgorithm( theAlgorithm, ContrastEnhancementMinMax, QgsRectangle(), SAMPLE_SIZE, theGenerateLookupTableFlag ); +} + +void QgsRasterLayer::setContrastEnhancementAlgorithm( QgsContrastEnhancement::ContrastEnhancementAlgorithm theAlgorithm, ContrastEnhancementLimits theLimits, QgsRectangle theExtent, int theSampleSize, bool theGenerateLookupTableFlag ) +{ + QgsDebugMsg( QString( "theAlgorithm = %1 theLimits = %2 theExtent.isEmpty() = %3" ).arg( theAlgorithm ).arg( theLimits ).arg( theExtent.isEmpty() ) ); if ( !mPipe.renderer() || !mDataProvider ) { return; } + QList myBands; + QList myEnhancements; + QgsSingleBandGrayRenderer* myGrayRenderer = 0; + QgsMultiBandColorRenderer* myMultiBandRenderer = 0; QString rendererType = mPipe.renderer()->type(); if ( rendererType == "singlebandgray" ) { - QgsSingleBandGrayRenderer* gr = dynamic_cast( mPipe.renderer() ); - if ( gr ) - { - int grayBand = gr->grayBand(); - if ( grayBand == -1 ) - { - return; - } - QgsRasterDataProvider::DataType dataType = ( QgsRasterDataProvider::DataType )mDataProvider->dataType( grayBand ); - QgsContrastEnhancement* ce = new QgsContrastEnhancement(( QgsContrastEnhancement::QgsRasterDataType )dataType ); - ce->setContrastEnhancementAlgorithm( theAlgorithm, theGenerateLookupTableFlag ); - ce->setMinimumValue( mDataProvider->minimumValue( grayBand ) ); - ce->setMaximumValue( mDataProvider->maximumValue( grayBand ) ); - gr->setContrastEnhancement( ce ); - } + myGrayRenderer = dynamic_cast( mPipe.renderer() ); + if ( !myGrayRenderer ) return; + myBands << myGrayRenderer->grayBand(); } else if ( rendererType == "multibandcolor" ) { - QgsMultiBandColorRenderer* cr = dynamic_cast( mPipe.renderer() ); - if ( cr ) + myMultiBandRenderer = dynamic_cast( mPipe.renderer() ); + if ( !myMultiBandRenderer ) return; + myBands << myMultiBandRenderer->redBand() << myMultiBandRenderer->greenBand() << myMultiBandRenderer->blueBand(); + } + + foreach( int myBand, myBands ) + { + if ( myBand != -1 ) { - //red enhancement - int redBand = cr->redBand(); - if ( redBand != -1 ) + QgsRasterDataProvider::DataType myType = ( QgsRasterDataProvider::DataType )mDataProvider->dataType( myBand ); + QgsContrastEnhancement* myEnhancement = new QgsContrastEnhancement(( QgsContrastEnhancement::QgsRasterDataType )myType ); + myEnhancement->setContrastEnhancementAlgorithm( theAlgorithm, theGenerateLookupTableFlag ); + + double myMin = std::numeric_limits::quiet_NaN(); + double myMax = std::numeric_limits::quiet_NaN(); + + if ( theLimits == ContrastEnhancementMinMax ) { - QgsRasterDataProvider::DataType redType = ( QgsRasterDataProvider::DataType )mDataProvider->dataType( redBand ); - QgsContrastEnhancement* redEnhancement = new QgsContrastEnhancement(( QgsContrastEnhancement::QgsRasterDataType )redType ); - redEnhancement->setContrastEnhancementAlgorithm( theAlgorithm, theGenerateLookupTableFlag ); - redEnhancement->setMinimumValue( mDataProvider->minimumValue( redBand ) ); - redEnhancement->setMaximumValue( mDataProvider->maximumValue( redBand ) ); - cr->setRedContrastEnhancement( redEnhancement ); + // minimumValue/maximumValue are not well defined (estimation) and will be removed + //myMin = mDataProvider->minimumValue( myBand ); + //myMax = mDataProvider->maximumValue( myBand ); + QgsRasterBandStats myRasterBandStats = mDataProvider->bandStatistics( myBand, QgsRasterBandStats::Min | QgsRasterBandStats::Max, theExtent, theSampleSize ); + myMin = myRasterBandStats.minimumValue; + myMax = myRasterBandStats.maximumValue; + } + else if ( theLimits == ContrastEnhancementStdDev ) + { + double myStdDev = 1; // make optional? + QgsRasterBandStats myRasterBandStats = mDataProvider->bandStatistics( myBand, QgsRasterBandStats::Mean | QgsRasterBandStats::StdDev, theExtent, theSampleSize ); + myMin = myRasterBandStats.mean - ( myStdDev * myRasterBandStats.stdDev ); + myMax = myRasterBandStats.mean + ( myStdDev * myRasterBandStats.stdDev ); + } + else if ( theLimits == ContrastEnhancementCumulativeCut ) + { + QSettings mySettings; + double myLower = mySettings.value( "/Raster/cumulativeCutLower", QString::number( CUMULATIVE_CUT_LOWER ) ).toDouble(); + double myUpper = mySettings.value( "/Raster/cumulativeCutUpper", QString::number( CUMULATIVE_CUT_UPPER ) ).toDouble(); + QgsDebugMsg( QString( "myLower = %1 myUpper = %2" ).arg( myLower ).arg( myUpper ) ); + mDataProvider->cumulativeCut( myBand, myLower, myUpper, myMin, myMax, theExtent, theSampleSize ); } - //green enhancement - int greenBand = cr->greenBand(); - if ( greenBand != -1 ) - { - QgsRasterDataProvider::DataType greenType = ( QgsRasterDataProvider::DataType )mDataProvider->dataType( cr->greenBand() ); - QgsContrastEnhancement* greenEnhancement = new QgsContrastEnhancement(( QgsContrastEnhancement::QgsRasterDataType )greenType ); - greenEnhancement->setContrastEnhancementAlgorithm( theAlgorithm, theGenerateLookupTableFlag ); - greenEnhancement->setMinimumValue( mDataProvider->minimumValue( greenBand ) ); - greenEnhancement->setMaximumValue( mDataProvider->maximumValue( greenBand ) ); - cr->setGreenContrastEnhancement( greenEnhancement ); - } - - //blue enhancement - int blueBand = cr->blueBand(); - if ( blueBand != -1 ) - { - QgsRasterDataProvider::DataType blueType = ( QgsRasterDataProvider::DataType )mDataProvider->dataType( cr->blueBand() ); - QgsContrastEnhancement* blueEnhancement = new QgsContrastEnhancement(( QgsContrastEnhancement::QgsRasterDataType )blueType ); - blueEnhancement->setContrastEnhancementAlgorithm( theAlgorithm, theGenerateLookupTableFlag ); - blueEnhancement->setMinimumValue( mDataProvider->minimumValue( blueBand ) ); - blueEnhancement->setMaximumValue( mDataProvider->maximumValue( blueBand ) ); - cr->setBlueContrastEnhancement( blueEnhancement ); - } + QgsDebugMsg( QString( "myBand = %1 myMin = %2 myMax = %3" ).arg( myBand ).arg( myMin ).arg( myMax ) ); + myEnhancement->setMinimumValue( myMin ); + myEnhancement->setMaximumValue( myMax ); + myEnhancements.append( myEnhancement ); } + else + { + myEnhancements.append( 0 ); + } + } + + if ( rendererType == "singlebandgray" ) + { + if ( myEnhancements.value( 0 ) ) myGrayRenderer->setContrastEnhancement( myEnhancements.value( 0 ) ); + } + else if ( rendererType == "multibandcolor" ) + { + if ( myEnhancements.value( 0 ) ) myMultiBandRenderer->setRedContrastEnhancement( myEnhancements.value( 0 ) ); + if ( myEnhancements.value( 1 ) ) myMultiBandRenderer->setGreenContrastEnhancement( myEnhancements.value( 1 ) ); + if ( myEnhancements.value( 2 ) ) myMultiBandRenderer->setBlueContrastEnhancement( myEnhancements.value( 2 ) ); } } diff --git a/src/core/raster/qgsrasterlayer.h b/src/core/raster/qgsrasterlayer.h index 8ef777a5c18..d28361809c8 100644 --- a/src/core/raster/qgsrasterlayer.h +++ b/src/core/raster/qgsrasterlayer.h @@ -174,6 +174,15 @@ class CORE_EXPORT QgsRasterLayer : public QgsMapLayer { Q_OBJECT public: + /** \brief Default cumulative cut lower limit */ + static const double CUMULATIVE_CUT_LOWER; + + /** \brief Default cumulative cut upper limit */ + static const double CUMULATIVE_CUT_UPPER; + + /** \brief Default sample size (number of pixels) for estimated statistics/histogram calculation */ + static const double SAMPLE_SIZE; + /** \brief Constructor. Provider is not set. */ QgsRasterLayer(); @@ -243,7 +252,15 @@ class CORE_EXPORT QgsRasterLayer : public QgsMapLayer Palette, Multiband, ColorLayer - } ; + }; + + /** \brief Contrast enhancement limits */ + enum ContrastEnhancementLimits + { + ContrastEnhancementMinMax, + ContrastEnhancementStdDev, + ContrastEnhancementCumulativeCut + }; /** \brief A list containing on ContrastEnhancement object per raster band in this raster layer */ typedef QList ContrastEnhancementList; @@ -548,10 +565,25 @@ class CORE_EXPORT QgsRasterLayer : public QgsMapLayer /** \brief Mutator for color shader algorithm */ Q_DECL_DEPRECATED void setColorShadingAlgorithm( QString theShaderAlgorithm ); - /** \brief Mutator for contrast enhancement algorithm */ + /** \brief Mutator for contrast enhancement algorithm using min/max */ + // TODO: remove in 2.0, replaced by following void setContrastEnhancementAlgorithm( QgsContrastEnhancement::ContrastEnhancementAlgorithm theAlgorithm, bool theGenerateLookupTableFlag = true ); + /** \brief Mutator for contrast enhancement algorithm + * @param theAlgorithm Contrast enhancement algorithm + * @param theLimits Limits + * @param theExtent Extent used to calculate limits, if empty, use full layer extent + * @param theSampleSize Size of data sample to calculate limits, if 0, use full resolution + * @param theGenerateLookupTableFlag Generate llokup table. */ + + + void setContrastEnhancementAlgorithm( QgsContrastEnhancement::ContrastEnhancementAlgorithm theAlgorithm, + ContrastEnhancementLimits theLimits = ContrastEnhancementMinMax, + QgsRectangle theExtent = QgsRectangle(), + int theSampleSize = SAMPLE_SIZE, + bool theGenerateLookupTableFlag = true ); + /** \brief Mutator for contrast enhancement algorithm */ void setContrastEnhancementAlgorithm( QString theAlgorithm, bool theGenerateLookupTableFlag = true ); diff --git a/src/core/raster/qgsrasterrenderer.h b/src/core/raster/qgsrasterrenderer.h index 2a7911c70c4..cdfa306b58d 100644 --- a/src/core/raster/qgsrasterrenderer.h +++ b/src/core/raster/qgsrasterrenderer.h @@ -22,6 +22,8 @@ #include "qgsrasterdataprovider.h" #include +#include "cpl_conv.h" + class QPainter; class QgsMapToPixel; class QgsRasterResampler; diff --git a/src/gui/raster/qgsrasterminmaxwidget.cpp b/src/gui/raster/qgsrasterminmaxwidget.cpp index da7eff2e3f4..dca7fb3e8fc 100644 --- a/src/gui/raster/qgsrasterminmaxwidget.cpp +++ b/src/gui/raster/qgsrasterminmaxwidget.cpp @@ -70,7 +70,7 @@ void QgsRasterMinMaxWidget::on_mLoadPushButton_clicked() } else if ( mStdDevRadioButton->isChecked() ) { - QgsRasterBandStats myRasterBandStats = mLayer->dataProvider()->bandStatistics( myBand, QgsRasterBandStats::Mean, myExtent, mySampleSize ); + QgsRasterBandStats myRasterBandStats = mLayer->dataProvider()->bandStatistics( myBand, QgsRasterBandStats::Mean | QgsRasterBandStats::StdDev, myExtent, mySampleSize ); double myStdDev = mStdDevSpinBox->value(); myMin = myRasterBandStats.mean - ( myStdDev * myRasterBandStats.stdDev ); myMax = myRasterBandStats.mean + ( myStdDev * myRasterBandStats.stdDev ); diff --git a/src/plugins/sqlanywhere/sasourceselect.cpp b/src/plugins/sqlanywhere/sasourceselect.cpp index 68260b37105..ec677b78c77 100644 --- a/src/plugins/sqlanywhere/sasourceselect.cpp +++ b/src/plugins/sqlanywhere/sasourceselect.cpp @@ -28,7 +28,6 @@ #include "sanewconnection.h" #include "qgsquerybuilder.h" -#include "qgisapp.h" #include "qgslogger.h" #include "qgsapplication.h" #include "qgscontexthelp.h" diff --git a/src/ui/qgisapp.ui b/src/ui/qgisapp.ui index e79176259c2..438e9480c4a 100644 --- a/src/ui/qgisapp.ui +++ b/src/ui/qgisapp.ui @@ -17,7 +17,7 @@ 0 0 1052 - 26 + 21 @@ -189,12 +189,6 @@ - - - &Raster - - - &Help @@ -220,6 +214,12 @@ + + + &Raster + + + @@ -385,8 +385,10 @@ false - + + + @@ -1748,6 +1750,30 @@ Shift thaws, Alt toggles, Ctl (Cmd) hides New Blank Project + + + + :/images/themes/default/mActionLocalCumulativeCutStretch.png:/images/themes/default/mActionLocalCumulativeCutStretch.png + + + Local Cumulative Cut Stretch + + + Local cumulative cut stretch using current extent, default limits and estimated values. + + + + + + :/images/themes/default/mActionFullCumulativeCutStretch.png:/images/themes/default/mActionFullCumulativeCutStretch.png + + + Full Dataset Cumulative Cut Stretch + + + Cumulative cut stretch using full dataset extent, default limits and estimated values. + + diff --git a/tests/testdata/control_images/expected_landsat_basic/expected_landsat_basic.png b/tests/testdata/control_images/expected_landsat_basic/expected_landsat_basic.png index 455f791ab93..00abbd07d7e 100644 Binary files a/tests/testdata/control_images/expected_landsat_basic/expected_landsat_basic.png and b/tests/testdata/control_images/expected_landsat_basic/expected_landsat_basic.png differ