1 /***************************************************************************
2                          qgsrasterminmaxwidget.h
3                          ---------------------------------
4     begin                : July 2012
5     copyright            : (C) 2012 by Radim Blazek
6     email                : radim dot blazek at gmail dot com
7  ***************************************************************************/
8 
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  ***************************************************************************/
17 
18 #include <QSettings>
19 #include <QMessageBox>
20 
21 #include "qgsrasterlayer.h"
22 #include "qgsrasterminmaxwidget.h"
23 #include "qgsmapcanvas.h"
24 #include "qgsrasterrenderer.h"
25 #include "qgsrasterdataprovider.h"
26 #include "qgsrasterminmaxorigin.h"
27 
28 const int IDX_WHOLE_RASTER = 0;
29 const int IDX_CURRENT_CANVAS = 1;
30 const int IDX_UPDATED_CANVAS = 2;
31 
QgsRasterMinMaxWidget(QgsRasterLayer * layer,QWidget * parent)32 QgsRasterMinMaxWidget::QgsRasterMinMaxWidget( QgsRasterLayer *layer, QWidget *parent )
33   : QWidget( parent )
34   , mLayer( layer )
35   , mLastRectangleValid( false )
36   , mBandsChanged( false )
37 {
38   QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
39   setupUi( this );
40   connect( mUserDefinedRadioButton, &QRadioButton::toggled, this, &QgsRasterMinMaxWidget::mUserDefinedRadioButton_toggled );
41   connect( mMinMaxRadioButton, &QRadioButton::toggled, this, &QgsRasterMinMaxWidget::mMinMaxRadioButton_toggled );
42   connect( mStdDevRadioButton, &QRadioButton::toggled, this, &QgsRasterMinMaxWidget::mStdDevRadioButton_toggled );
43   connect( mCumulativeCutRadioButton, &QRadioButton::toggled, this, &QgsRasterMinMaxWidget::mCumulativeCutRadioButton_toggled );
44   connect( mStatisticsExtentCombo, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsRasterMinMaxWidget::mStatisticsExtentCombo_currentIndexChanged );
45   connect( mCumulativeCutLowerDoubleSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsRasterMinMaxWidget::mCumulativeCutLowerDoubleSpinBox_valueChanged );
46   connect( mCumulativeCutUpperDoubleSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsRasterMinMaxWidget::mCumulativeCutUpperDoubleSpinBox_valueChanged );
47   connect( mStdDevSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsRasterMinMaxWidget::mStdDevSpinBox_valueChanged );
48   connect( cboAccuracy, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsRasterMinMaxWidget::cboAccuracy_currentIndexChanged );
49 
50   QgsRasterMinMaxOrigin defaultMinMaxOrigin;
51   setFromMinMaxOrigin( defaultMinMaxOrigin );
52 }
53 
setMapCanvas(QgsMapCanvas * canvas)54 void QgsRasterMinMaxWidget::setMapCanvas( QgsMapCanvas *canvas )
55 {
56   mCanvas = canvas;
57 }
58 
mapCanvas()59 QgsMapCanvas *QgsRasterMinMaxWidget::mapCanvas()
60 {
61   return mCanvas;
62 }
63 
setBands(const QList<int> & bands)64 void QgsRasterMinMaxWidget::setBands( const QList<int> &bands )
65 {
66   mBandsChanged = bands != mBands;
67   mBands = bands;
68 }
69 
extent()70 QgsRectangle QgsRasterMinMaxWidget::extent()
71 {
72   const int nExtentIdx = mStatisticsExtentCombo->currentIndex();
73   if ( nExtentIdx != IDX_CURRENT_CANVAS && nExtentIdx != IDX_UPDATED_CANVAS )
74     return QgsRectangle();
75 
76   if ( mLayer && mCanvas )
77     return mCanvas->mapSettings().outputExtentToLayerExtent( mLayer, mCanvas->extent() );
78   else if ( mCanvas )
79     return mCanvas->extent();
80   else
81     return QgsRectangle();
82 }
83 
userHasSetManualMinMaxValues()84 void QgsRasterMinMaxWidget::userHasSetManualMinMaxValues()
85 {
86   mUserDefinedRadioButton->setChecked( true );
87   mStatisticsExtentCombo->setCurrentIndex( IDX_WHOLE_RASTER );
88 }
89 
mUserDefinedRadioButton_toggled(bool toggled)90 void QgsRasterMinMaxWidget::mUserDefinedRadioButton_toggled( bool toggled )
91 {
92   mStatisticsExtentCombo->setEnabled( !toggled );
93   cboAccuracy->setEnabled( !toggled );
94   emit widgetChanged();
95 }
96 
setFromMinMaxOrigin(const QgsRasterMinMaxOrigin & minMaxOrigin)97 void QgsRasterMinMaxWidget::setFromMinMaxOrigin( const QgsRasterMinMaxOrigin &minMaxOrigin )
98 {
99   switch ( minMaxOrigin.limits() )
100   {
101     case QgsRasterMinMaxOrigin::None:
102       mUserDefinedRadioButton->setChecked( true );
103       break;
104 
105     case QgsRasterMinMaxOrigin::MinMax:
106       mMinMaxRadioButton->setChecked( true );
107       break;
108 
109     case QgsRasterMinMaxOrigin::StdDev:
110       mStdDevRadioButton->setChecked( true );
111       break;
112 
113     case QgsRasterMinMaxOrigin::CumulativeCut:
114       mCumulativeCutRadioButton->setChecked( true );
115       break;
116   }
117 
118   switch ( minMaxOrigin.extent() )
119   {
120     case QgsRasterMinMaxOrigin::WholeRaster:
121       mStatisticsExtentCombo->setCurrentIndex( IDX_WHOLE_RASTER );
122       break;
123 
124     case QgsRasterMinMaxOrigin::CurrentCanvas:
125       mStatisticsExtentCombo->setCurrentIndex( IDX_CURRENT_CANVAS );
126       break;
127 
128     case QgsRasterMinMaxOrigin::UpdatedCanvas:
129       mStatisticsExtentCombo->setCurrentIndex( IDX_UPDATED_CANVAS );
130       break;
131   }
132 
133   mCumulativeCutLowerDoubleSpinBox->setValue( 100.0 * minMaxOrigin.cumulativeCutLower() );
134   mCumulativeCutUpperDoubleSpinBox->setValue( 100.0 * minMaxOrigin.cumulativeCutUpper() );
135   mStdDevSpinBox->setValue( minMaxOrigin.stdDevFactor() );
136 
137   cboAccuracy->setCurrentIndex( minMaxOrigin.statAccuracy() == QgsRasterMinMaxOrigin::Estimated ? 0 : 1 );
138 }
139 
minMaxOrigin()140 QgsRasterMinMaxOrigin QgsRasterMinMaxWidget::minMaxOrigin()
141 {
142   QgsRasterMinMaxOrigin minMaxOrigin;
143 
144   if ( mMinMaxRadioButton->isChecked() )
145     minMaxOrigin.setLimits( QgsRasterMinMaxOrigin::MinMax );
146   else if ( mStdDevRadioButton->isChecked() )
147     minMaxOrigin.setLimits( QgsRasterMinMaxOrigin::StdDev );
148   else if ( mCumulativeCutRadioButton->isChecked() )
149     minMaxOrigin.setLimits( QgsRasterMinMaxOrigin::CumulativeCut );
150   else
151     minMaxOrigin.setLimits( QgsRasterMinMaxOrigin::None );
152 
153   switch ( mStatisticsExtentCombo->currentIndex() )
154   {
155     case IDX_WHOLE_RASTER:
156     default:
157       minMaxOrigin.setExtent( QgsRasterMinMaxOrigin::WholeRaster );
158       break;
159     case IDX_CURRENT_CANVAS:
160       minMaxOrigin.setExtent( QgsRasterMinMaxOrigin::CurrentCanvas );
161       break;
162     case IDX_UPDATED_CANVAS:
163       minMaxOrigin.setExtent( QgsRasterMinMaxOrigin::UpdatedCanvas );
164       break;
165   }
166 
167   if ( cboAccuracy->currentIndex() == 0 )
168     minMaxOrigin.setStatAccuracy( QgsRasterMinMaxOrigin::Estimated );
169   else
170     minMaxOrigin.setStatAccuracy( QgsRasterMinMaxOrigin::Exact );
171 
172   minMaxOrigin.setCumulativeCutLower(
173     mCumulativeCutLowerDoubleSpinBox->value() / 100.0 );
174   minMaxOrigin.setCumulativeCutUpper(
175     mCumulativeCutUpperDoubleSpinBox->value() / 100.0 );
176   minMaxOrigin.setStdDevFactor( mStdDevSpinBox->value() );
177 
178   return minMaxOrigin;
179 }
180 
doComputations()181 void QgsRasterMinMaxWidget::doComputations()
182 {
183   QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
184   if ( !mLayer->dataProvider() )
185     return;
186 
187   QgsRectangle myExtent = extent(); // empty == full
188   int mySampleSize = sampleSize(); // 0 == exact
189 
190   QgsRasterMinMaxOrigin newMinMaxOrigin = minMaxOrigin();
191   if ( mLastRectangleValid && mLastRectangle == myExtent &&
192        mLastMinMaxOrigin == newMinMaxOrigin &&
193        !mBandsChanged )
194   {
195     QgsDebugMsg( QStringLiteral( "Does not need to redo statistics computations" ) );
196     return;
197   }
198 
199   mLastRectangleValid = true;
200   mLastRectangle = myExtent;
201   mLastMinMaxOrigin = newMinMaxOrigin;
202   mBandsChanged = false;
203 
204   const auto constMBands = mBands;
205   for ( int myBand : constMBands )
206   {
207     QgsDebugMsg( QStringLiteral( "myBand = %1" ).arg( myBand ) );
208     if ( myBand < 1 || myBand > mLayer->dataProvider()->bandCount() )
209     {
210       continue;
211     }
212     double myMin = std::numeric_limits<double>::quiet_NaN();
213     double myMax = std::numeric_limits<double>::quiet_NaN();
214 
215     bool updateMinMax = false;
216     if ( mCumulativeCutRadioButton->isChecked() )
217     {
218       updateMinMax = true;
219       double myLower = mCumulativeCutLowerDoubleSpinBox->value() / 100.0;
220       double myUpper = mCumulativeCutUpperDoubleSpinBox->value() / 100.0;
221       mLayer->dataProvider()->cumulativeCut( myBand, myLower, myUpper, myMin, myMax, myExtent, mySampleSize );
222     }
223     else if ( mMinMaxRadioButton->isChecked() )
224     {
225       updateMinMax = true;
226       // TODO: consider provider minimum/maximumValue() (has to be defined well in povider)
227       QgsRasterBandStats myRasterBandStats = mLayer->dataProvider()->bandStatistics( myBand, QgsRasterBandStats::Min | QgsRasterBandStats::Max, myExtent, mySampleSize );
228       myMin = myRasterBandStats.minimumValue;
229       myMax = myRasterBandStats.maximumValue;
230     }
231     else if ( mStdDevRadioButton->isChecked() )
232     {
233       updateMinMax = true;
234       QgsRasterBandStats myRasterBandStats = mLayer->dataProvider()->bandStatistics( myBand, QgsRasterBandStats::Mean | QgsRasterBandStats::StdDev, myExtent, mySampleSize );
235       double myStdDev = mStdDevSpinBox->value();
236       myMin = myRasterBandStats.mean - ( myStdDev * myRasterBandStats.stdDev );
237       myMax = myRasterBandStats.mean + ( myStdDev * myRasterBandStats.stdDev );
238     }
239 
240     if ( updateMinMax )
241       emit load( myBand, myMin, myMax );
242   }
243 }
244 
hideUpdatedExtent()245 void QgsRasterMinMaxWidget::hideUpdatedExtent()
246 {
247   mStatisticsExtentCombo->removeItem( IDX_UPDATED_CANVAS );
248 }
249