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