QGIS API Documentation 3.41.0-Master (3440c17df1d)
Loading...
Searching...
No Matches
qgsrasterhistogramwidget.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsrasterhistogramwidget.cpp
3 ---------------------------
4 begin : July 2012
5 copyright : (C) 2012 by Etienne Tourigny
6 email : etourigny dot dev 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 "qgsapplication.h"
19#include "qgsguiutils.h"
22#include "moc_qgsrasterhistogramwidget.cpp"
24#include "qgsdoublevalidator.h"
25#include "qgssettings.h"
26#include "qgsrasterlayer.h"
27
28#include <QMenu>
29#include <QFileInfo>
30#include <QDir>
31#include <QPainter>
32#include <QActionGroup>
33#include <QRandomGenerator>
34
35// QWT Charting widget
36#include <qwt_global.h>
37#include <qwt_plot_canvas.h>
38#include <qwt_legend.h>
39#include <qwt_plot.h>
40#include <qwt_plot_curve.h>
41#include <qwt_plot_grid.h>
42#include <qwt_plot_marker.h>
43#include <qwt_plot_picker.h>
44#include <qwt_picker_machine.h>
45#include <qwt_plot_zoomer.h>
46#include <qwt_plot_layout.h>
47#include <qwt_plot_renderer.h>
48#include <qwt_plot_histogram.h>
49#include <qwt_scale_div.h>
50
51#ifdef Q_OS_WIN
52#include <time.h>
53#endif
54
55constexpr int SAMPLE_SIZE = 250000; // number of sample cells
56
58 : QgsMapLayerConfigWidget( lyr, nullptr, parent )
59 , mRasterLayer( lyr )
60
61{
62 setupUi( this );
63 connect( mSaveAsImageButton, &QToolButton::clicked, this, &QgsRasterHistogramWidget::mSaveAsImageButton_clicked );
64 connect( cboHistoBand, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsRasterHistogramWidget::cboHistoBand_currentIndexChanged );
65 connect( btnHistoMin, &QToolButton::toggled, this, &QgsRasterHistogramWidget::btnHistoMin_toggled );
66 connect( btnHistoMax, &QToolButton::toggled, this, &QgsRasterHistogramWidget::btnHistoMax_toggled );
67 connect( btnHistoCompute, &QPushButton::clicked, this, &QgsRasterHistogramWidget::btnHistoCompute_clicked );
68
69 mSaveAsImageButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionFileSave.svg" ) ) );
70
71 mRendererWidget = nullptr;
72 mRendererName = QStringLiteral( "singlebandgray" );
73
74 mHistoMin = 0;
75 mHistoMax = 0;
76
77 mHistoPicker = nullptr;
78 mHistoZoomer = nullptr;
79 mHistoMarkerMin = nullptr;
80 mHistoMarkerMax = nullptr;
81
82 const QgsSettings settings;
83 mHistoShowMarkers = settings.value( QStringLiteral( "Raster/histogram/showMarkers" ), false ).toBool();
84 // mHistoLoadApplyAll = settings.value( "/Raster/histogram/loadApplyAll", false ).toBool();
85 mHistoZoomToMinMax = settings.value( QStringLiteral( "Raster/histogram/zoomToMinMax" ), false ).toBool();
86 mHistoUpdateStyleToMinMax = settings.value( QStringLiteral( "Raster/histogram/updateStyleToMinMax" ), true ).toBool();
87 mHistoDrawLines = settings.value( QStringLiteral( "Raster/histogram/drawLines" ), true ).toBool();
88 // mHistoShowBands = (HistoShowBands) settings.value( "/Raster/histogram/showBands", (int) ShowAll ).toInt();
89 mHistoShowBands = ShowAll;
90
91 bool isInt = true;
92 if ( true )
93 {
94 //band selector
95 const int myBandCountInt = mRasterLayer->bandCount();
96 for ( int myIteratorInt = 1;
97 myIteratorInt <= myBandCountInt;
98 ++myIteratorInt )
99 {
100 cboHistoBand->addItem( mRasterLayer->bandName( myIteratorInt ) );
101 const Qgis::DataType mySrcDataType = mRasterLayer->dataProvider()->sourceDataType( myIteratorInt );
102 if ( !( mySrcDataType == Qgis::DataType::Byte ||
103 mySrcDataType == Qgis::DataType::Int8 ||
104 mySrcDataType == Qgis::DataType::Int16 || mySrcDataType == Qgis::DataType::Int32 ||
105 mySrcDataType == Qgis::DataType::UInt16 || mySrcDataType == Qgis::DataType::UInt32 ) )
106 isInt = false;
107 }
108
109 // histo min/max selectors
110 leHistoMin->setValidator( new QgsDoubleValidator( this ) );
111 leHistoMax->setValidator( new QgsDoubleValidator( this ) );
112 // this might generate many refresh events! test..
113 // connect( leHistoMin, SIGNAL( textChanged( const QString & ) ), this, SLOT( updateHistoMarkers() ) );
114 // connect( leHistoMax, SIGNAL( textChanged( const QString & ) ), this, SLOT( updateHistoMarkers() ) );
115 // connect( leHistoMin, SIGNAL( textChanged( const QString & ) ), this, SLOT( applyHistoMin() ) );
116 // connect( leHistoMax, SIGNAL( textChanged( const QString & ) ), this, SLOT( applyHistoMax() ) );
117 connect( leHistoMin, &QLineEdit::editingFinished, this, &QgsRasterHistogramWidget::applyHistoMin );
118 connect( leHistoMax, &QLineEdit::editingFinished, this, &QgsRasterHistogramWidget::applyHistoMax );
119
120 // histo actions
121 // TODO move/add options to qgis options dialog
122 QMenu *menu = new QMenu( this );
123 menu->setSeparatorsCollapsible( false );
124 btnHistoActions->setMenu( menu );
125 QActionGroup *group = nullptr;
126 QAction *action = nullptr;
127
128 // min/max options
129 group = new QActionGroup( this );
130 group->setExclusive( false );
131 connect( group, &QActionGroup::triggered, this, &QgsRasterHistogramWidget::histoActionTriggered );
132 action = new QAction( tr( "Min/Max options" ), group );
133 action->setSeparator( true );
134 menu->addAction( action );
135 action = new QAction( tr( "Always show min/max markers" ), group );
136 action->setData( QVariant( "Show markers" ) );
137 action->setCheckable( true );
138 action->setChecked( mHistoShowMarkers );
139 menu->addAction( action );
140 action = new QAction( tr( "Zoom to min/max" ), group );
141 action->setData( QVariant( "Zoom min_max" ) );
142 action->setCheckable( true );
143 action->setChecked( mHistoZoomToMinMax );
144 menu->addAction( action );
145 action = new QAction( tr( "Update style to min/max" ), group );
146 action->setData( QVariant( "Update min_max" ) );
147 action->setCheckable( true );
148 action->setChecked( mHistoUpdateStyleToMinMax );
149 menu->addAction( action );
150
151 // visibility options
152 group = new QActionGroup( this );
153 group->setExclusive( false );
154 connect( group, &QActionGroup::triggered, this, &QgsRasterHistogramWidget::histoActionTriggered );
155 action = new QAction( tr( "Visibility" ), group );
156 action->setSeparator( true );
157 menu->addAction( action );
158 group = new QActionGroup( this );
159 group->setExclusive( true ); // these options are exclusive
160 connect( group, &QActionGroup::triggered, this, &QgsRasterHistogramWidget::histoActionTriggered );
161 action = new QAction( tr( "Show all bands" ), group );
162 action->setData( QVariant( "Show all" ) );
163 action->setCheckable( true );
164 action->setChecked( mHistoShowBands == ShowAll );
165 menu->addAction( action );
166 action = new QAction( tr( "Show RGB/Gray band(s)" ), group );
167 action->setData( QVariant( "Show RGB" ) );
168 action->setCheckable( true );
169 action->setChecked( mHistoShowBands == ShowRGB );
170 menu->addAction( action );
171 action = new QAction( tr( "Show selected band" ), group );
172 action->setData( QVariant( "Show selected" ) );
173 action->setCheckable( true );
174 action->setChecked( mHistoShowBands == ShowSelected );
175 menu->addAction( action );
176
177 // display options
178 group = new QActionGroup( this );
179 group->setExclusive( false );
180 connect( group, &QActionGroup::triggered, this, &QgsRasterHistogramWidget::histoActionTriggered );
181 action = new QAction( tr( "Display" ), group );
182 action->setSeparator( true );
183 menu->addAction( action );
184 // should we plot as histogram instead of line plot? (int data only)
185 action = new QAction( QString(), group );
186 action->setData( QVariant( "Draw lines" ) );
187 if ( isInt )
188 {
189 action->setText( tr( "Draw as lines" ) );
190 action->setCheckable( true );
191 action->setChecked( mHistoDrawLines );
192 }
193 else
194 {
195 action->setText( tr( "Draw as lines (only int layers)" ) );
196 action->setEnabled( false );
197 }
198 menu->addAction( action );
199
200 // actions
201 action = new QAction( tr( "Actions" ), group );
202 action->setSeparator( true );
203 menu->addAction( action );
204
205 // load actions
206 group = new QActionGroup( this );
207 group->setExclusive( false );
208 connect( group, &QActionGroup::triggered, this, &QgsRasterHistogramWidget::histoActionTriggered );
209 action = new QAction( tr( "Reset" ), group );
210 action->setData( QVariant( "Load reset" ) );
211 menu->addAction( action );
212
213 // these actions have been disabled for api cleanup, restore them eventually
214 // TODO restore these in qgis 2.4
215#if 0
216 // Load min/max needs 3 params (method, extent, accuracy), cannot put it in single item
217 action = new QAction( tr( "Load min/max" ), group );
218 action->setSeparator( true );
219 menu->addAction( action );
220 action = new QAction( tr( "Estimate (faster)" ), group );
221 action->setData( QVariant( "Load estimate" ) );
222 menu->addAction( action );
223 action = new QAction( tr( "Actual (slower)" ), group );
224 action->setData( QVariant( "Load actual" ) );
225 menu->addAction( action );
226 action = new QAction( tr( "Current extent" ), group );
227 action->setData( QVariant( "Load extent" ) );
228 menu->addAction( action );
229 action = new QAction( tr( "Use stddev (1.0)" ), group );
230 action->setData( QVariant( "Load 1 stddev" ) );
231 menu->addAction( action );
232 action = new QAction( tr( "Use stddev (custom)" ), group );
233 action->setData( QVariant( "Load stddev" ) );
234 menu->addAction( action );
235 action = new QAction( tr( "Load for each band" ), group );
236 action->setData( QVariant( "Load apply all" ) );
237 action->setCheckable( true );
238 action->setChecked( mHistoLoadApplyAll );
239 menu->addAction( action );
240#endif
241
242 //others
243 action = new QAction( tr( "Recompute Histogram" ), group );
244 action->setData( QVariant( "Compute histogram" ) );
245 menu->addAction( action );
246
247 }
248
249} // QgsRasterHistogramWidget ctor
250
252{
253 mRendererName = name;
254 mRendererWidget = rendererWidget;
256 cboHistoBand_currentIndexChanged( -1 );
257}
258
260{
261 if ( activeFlag )
262 {
264 cboHistoBand_currentIndexChanged( -1 );
265 }
266 else
267 {
268 if ( QApplication::overrideCursor() )
269 QApplication::restoreOverrideCursor();
270 btnHistoMin->setChecked( false );
271 btnHistoMax->setChecked( false );
272 }
273}
274
275void QgsRasterHistogramWidget::btnHistoCompute_clicked()
276{
277// Histogram computation can be called either by clicking the "Compute Histogram" button
278// which is only visible if there is no cached histogram or by calling the
279// "Compute Histogram" action. Due to limitations in the gdal api, it is not possible
280// to re-calculate the histogram if it has already been calculated
281 computeHistogram( true );
283}
284
285// Compute the number of bins
286// Logic partially borrowed to QgsRasterInterface::initHistogram(),
287// but with a limitation to 1000 bins. Otherwise the histogram will be
288// unreadable (see https://github.com/qgis/QGIS/issues/38298)
289// NOTE: the number of bins should probably be let to the user, and/or adaptative
290// to the width in pixels of the chart.
291static int getBinCount( QgsRasterInterface *rasterInterface,
292 int bandNo,
293 int sampleSize )
294{
295 const Qgis::DataType mySrcDataType = rasterInterface->sourceDataType( bandNo );
296 const double statsMin = mySrcDataType == Qgis::DataType::Byte ? 0 :
297 rasterInterface->bandStatistics( bandNo, Qgis::RasterBandStatistic::Min, QgsRectangle(), sampleSize ).minimumValue;
298 const double statsMax = mySrcDataType == Qgis::DataType::Byte ? 255 :
299 rasterInterface->bandStatistics( bandNo, Qgis::RasterBandStatistic::Max, QgsRectangle(), sampleSize ).maximumValue;
300 const QgsRectangle extent( rasterInterface->extent() );
301
302 // Calc resolution from sampleSize
303 double xRes, yRes;
304 xRes = yRes = std::sqrt( ( static_cast<double>( extent.width( ) ) * extent.height() ) / sampleSize );
305
306 // But limit by physical resolution
307 if ( rasterInterface->capabilities() & Qgis::RasterInterfaceCapability::Size )
308 {
309 const double srcXRes = extent.width() / rasterInterface->xSize();
310 const double srcYRes = extent.height() / rasterInterface->ySize();
311 if ( xRes < srcXRes ) xRes = srcXRes;
312 if ( yRes < srcYRes ) yRes = srcYRes;
313 }
314
315 const int histogramWidth = static_cast <int>( extent.width() / xRes );
316 const int histogramHeight = static_cast <int>( extent.height() / yRes );
317
318 int binCount = static_cast<int>( std::min( static_cast<qint64>( 1000 ),
319 static_cast<qint64>( histogramWidth ) * histogramHeight ) );
320
321 if ( mySrcDataType == Qgis::DataType::Int16 || mySrcDataType == Qgis::DataType::Int32 ||
322 mySrcDataType == Qgis::DataType::UInt16 || mySrcDataType == Qgis::DataType::UInt32 )
323 {
324 binCount = static_cast<int>( std::min( static_cast<qint64>( binCount ),
325 static_cast<qint64>( std::ceil( statsMax - statsMin + 1 ) ) ) );
326 }
327
328 return binCount;
329}
330
332{
333
334 //bool myIgnoreOutOfRangeFlag = true;
335 //bool myThoroughBandScanFlag = false;
336 const int myBandCountInt = mRasterLayer->bandCount();
337
338 // if forceComputeFlag = false make sure raster has cached histogram, else return false
339 if ( ! forceComputeFlag )
340 {
341 for ( int myIteratorInt = 1;
342 myIteratorInt <= myBandCountInt;
343 ++myIteratorInt )
344 {
345 const int sampleSize = SAMPLE_SIZE; // number of sample cells
346 const int binCount = getBinCount( mRasterLayer->dataProvider(), myIteratorInt, sampleSize );
347 if ( !mRasterLayer->dataProvider()->hasHistogram( myIteratorInt, binCount, std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN(), QgsRectangle(), sampleSize ) )
348 {
349 QgsDebugMsgLevel( QStringLiteral( "band %1 does not have cached histo" ).arg( myIteratorInt ), 2 );
350 return false;
351 }
352 }
353 }
354
355 // compute histogram
356 stackedWidget2->setCurrentIndex( 1 );
357
358 const std::unique_ptr< QgsRasterBlockFeedback > feedback( new QgsRasterBlockFeedback() );
359 connect( feedback.get(), &QgsRasterBlockFeedback::progressChanged, mHistogramProgress, &QProgressBar::setValue );
360 QApplication::setOverrideCursor( Qt::WaitCursor );
361
362 for ( int myIteratorInt = 1;
363 myIteratorInt <= myBandCountInt;
364 ++myIteratorInt )
365 {
366 const int sampleSize = SAMPLE_SIZE; // number of sample cells
367 const int binCount = getBinCount( mRasterLayer->dataProvider(), myIteratorInt, sampleSize );
368 mRasterLayer->dataProvider()->histogram( myIteratorInt, binCount, std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN(), QgsRectangle(), sampleSize, false, feedback.get() );
369 }
370
371 // mHistogramProgress->hide();
372 stackedWidget2->setCurrentIndex( 0 );
373 QApplication::restoreOverrideCursor();
374
375 return true;
376}
377
378
380{
381 // Explanation:
382 // We use the gdal histogram creation routine is called for each selected
383 // layer. Currently the hist is hardcoded to create 256 bins. Each bin stores
384 // the total number of cells that fit into the range defined by that bin.
385 //
386 // The graph routine below determines the greatest number of pixels in any given
387 // bin in all selected layers, and the min. It then draws a scaled line between min
388 // and max - scaled to image height. 1 line drawn per selected band
389 //
390 const int myBandCountInt = mRasterLayer->bandCount();
391
392
393 if ( ! computeHistogram( false ) )
394 {
395 QgsDebugMsgLevel( QStringLiteral( "raster does not have cached histogram" ), 2 );
396 stackedWidget2->setCurrentIndex( 2 );
397 return;
398 }
399
400 // clear plot
401 mpPlot->detachItems();
402
403 //ensure all children get removed
404 mpPlot->setAutoDelete( true );
405 mpPlot->setTitle( QObject::tr( "Raster Histogram" ) );
406 mpPlot->insertLegend( new QwtLegend(), QwtPlot::BottomLegend );
407 // Set axis titles
408 mpPlot->setAxisTitle( QwtPlot::xBottom, QObject::tr( "Pixel Value" ) );
409 mpPlot->setAxisTitle( QwtPlot::yLeft, QObject::tr( "Frequency" ) );
410 mpPlot->setAxisAutoScale( QwtPlot::yLeft );
411
412 // x axis scale only set after computing global min/max across bands (see below)
413 // add a grid
414 QwtPlotGrid *myGrid = new QwtPlotGrid();
415 myGrid->attach( mpPlot );
416
417 // make colors list
418 mHistoColors.clear();
419 mHistoColors << Qt::black; // first element, not used
420 QVector<QColor> myColors;
421 myColors << Qt::red << Qt::green << Qt::blue << Qt::magenta << Qt::darkYellow << Qt::cyan;
422
423 // make sure colors are always the same for a given band count
424 QRandomGenerator colorGenerator( myBandCountInt * 100 );
425 while ( myColors.size() <= myBandCountInt )
426 {
427 myColors <<
428 QColor( colorGenerator.bounded( 1, 256 ),
429 colorGenerator.bounded( 1, 256 ),
430 colorGenerator.bounded( 1, 256 ) );
431 }
432
433 // assign colors to each band, depending on the current RGB/gray band selection
434 // grayscale
435 QList< int > mySelectedBands = rendererSelectedBands();
436 if ( mRendererName == QLatin1String( "singlebandgray" ) )
437 {
438 const int myGrayBand = mySelectedBands[0];
439 for ( int i = 1; i <= myBandCountInt; i++ )
440 {
441 if ( i == myGrayBand )
442 {
443 mHistoColors << Qt::darkGray;
444 cboHistoBand->setItemData( i - 1, QColor( Qt::darkGray ), Qt::ForegroundRole );
445 }
446 else
447 {
448 if ( ! myColors.isEmpty() )
449 {
450 mHistoColors << myColors.first();
451 myColors.pop_front();
452 }
453 else
454 {
455 mHistoColors << Qt::black;
456 }
457 cboHistoBand->setItemData( i - 1, QColor( Qt::black ), Qt::ForegroundRole );
458 }
459 }
460 }
461 // RGB
462 else if ( mRendererName == QLatin1String( "multibandcolor" ) )
463 {
464 const int myRedBand = mySelectedBands[0];
465 const int myGreenBand = mySelectedBands[1];
466 const int myBlueBand = mySelectedBands[2];
467 // remove RGB, which are reserved for the actual RGB bands
468 // show name of RGB bands in appropriate color in bold
469 myColors.remove( 0, 3 );
470 for ( int i = 1; i <= myBandCountInt; i++ )
471 {
472 QColor myColor;
473 if ( i == myRedBand )
474 myColor = Qt::red;
475 else if ( i == myGreenBand )
476 myColor = Qt::green;
477 else if ( i == myBlueBand )
478 myColor = Qt::blue;
479 else
480 {
481 if ( ! myColors.isEmpty() )
482 {
483 myColor = myColors.first();
484 myColors.pop_front();
485 }
486 else
487 {
488 myColor = Qt::black;
489 }
490 cboHistoBand->setItemData( i - 1, QColor( Qt::black ), Qt::ForegroundRole );
491 }
492 if ( i == myRedBand || i == myGreenBand || i == myBlueBand )
493 {
494 cboHistoBand->setItemData( i - 1, myColor, Qt::ForegroundRole );
495 }
496 mHistoColors << myColor;
497 }
498 }
499 else
500 {
501 mHistoColors << myColors;
502 }
503
504 //
505 //now draw actual graphs
506 //
507
508 //sometimes there are more bins than needed
509 //we find out the last one that actually has data in it
510 //so we can discard the rest and set the x-axis scales correctly
511 //
512 // scan through to get counts from layers' histograms
513 //
514 mHistoMin = 0;
515 mHistoMax = 0;
516 bool myFirstIteration = true;
517 /* Gets selected band list, if mHistoShowBands != ShowAll */
518 mySelectedBands = histoSelectedBands();
519 double myBinXStep = 1;
520 double myBinX = 0;
521
522 for ( int bandNumber = 1;
523 bandNumber <= myBandCountInt;
524 ++bandNumber )
525 {
526 /* skip this band if mHistoShowBands != ShowAll and this band is not selected */
527 if ( mHistoShowBands != ShowAll )
528 {
529 if ( ! mySelectedBands.contains( bandNumber ) )
530 continue;
531 }
532
533 const int sampleSize = SAMPLE_SIZE; // number of sample cells
534
535 const std::unique_ptr< QgsRasterBlockFeedback > feedback( new QgsRasterBlockFeedback() );
536 connect( feedback.get(), &QgsRasterBlockFeedback::progressChanged, mHistogramProgress, &QProgressBar::setValue );
537
538 const int binCount = getBinCount( mRasterLayer->dataProvider(), bandNumber, sampleSize );
539 const QgsRasterHistogram myHistogram = mRasterLayer->dataProvider()->histogram( bandNumber, binCount, std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN(), QgsRectangle(), sampleSize, false, feedback.get() );
540
541 QgsDebugMsgLevel( QStringLiteral( "got raster histo for band %1 : min=%2 max=%3 count=%4" ).arg( bandNumber ).arg( myHistogram.minimum ).arg( myHistogram.maximum ).arg( myHistogram.binCount ), 2 );
542
543 const Qgis::DataType mySrcDataType = mRasterLayer->dataProvider()->sourceDataType( bandNumber );
544 bool myDrawLines = true;
545 if ( ! mHistoDrawLines &&
546 ( mySrcDataType == Qgis::DataType::Byte ||
547 mySrcDataType == Qgis::DataType::Int16 || mySrcDataType == Qgis::DataType::Int32 ||
548 mySrcDataType == Qgis::DataType::UInt16 || mySrcDataType == Qgis::DataType::UInt32 ) )
549 {
550 myDrawLines = false;
551 }
552
553 QwtPlotCurve *mypCurve = nullptr;
554 if ( myDrawLines )
555 {
556 mypCurve = new QwtPlotCurve( tr( "Band %1" ).arg( bandNumber ) );
557 //mypCurve->setCurveAttribute( QwtPlotCurve::Fitted );
558 mypCurve->setRenderHint( QwtPlotItem::RenderAntialiased );
559 mypCurve->setPen( QPen( mHistoColors.at( bandNumber ) ) );
560 }
561
562 QwtPlotHistogram *mypHisto = nullptr;
563 if ( ! myDrawLines )
564 {
565 mypHisto = new QwtPlotHistogram( tr( "Band %1" ).arg( bandNumber ) );
566 mypHisto->setRenderHint( QwtPlotItem::RenderAntialiased );
567 //mypHisto->setPen( QPen( mHistoColors.at( myIteratorInt ) ) );
568 mypHisto->setPen( QPen( Qt::lightGray ) );
569 // this is needed in order to see the colors in the legend
570 mypHisto->setBrush( QBrush( mHistoColors.at( bandNumber ) ) );
571 }
572
573 QVector<QPointF> data;
574 QVector<QwtIntervalSample> dataHisto;
575
576 // calculate first bin x value and bin step size
577 myBinXStep = ( myHistogram.maximum - myHistogram.minimum ) / myHistogram.binCount;
578 myBinX = myHistogram.minimum + myBinXStep / 2.0;
579
580 for ( int myBin = 0; myBin < myHistogram.binCount; myBin++ )
581 {
582 const int myBinValue = myHistogram.histogramVector.at( myBin );
583 if ( myDrawLines )
584 {
585 data << QPointF( myBinX, myBinValue );
586 }
587 else
588 {
589 dataHisto << QwtIntervalSample( myBinValue, myBinX - myBinXStep / 2.0, myBinX + myBinXStep / 2.0 );
590 }
591 myBinX += myBinXStep;
592 }
593
594 if ( myDrawLines )
595 {
596 mypCurve->setSamples( data );
597 mypCurve->attach( mpPlot );
598 }
599 else
600 {
601 mypHisto->setSamples( dataHisto );
602 mypHisto->attach( mpPlot );
603 }
604
605 if ( myFirstIteration || mHistoMin > myHistogram.minimum )
606 {
607 mHistoMin = myHistogram.minimum;
608 }
609 if ( myFirstIteration || mHistoMax < myHistogram.maximum )
610 {
611 mHistoMax = myHistogram.maximum;
612 }
613 QgsDebugMsgLevel( QStringLiteral( "computed histo min = %1 max = %2" ).arg( mHistoMin ).arg( mHistoMax ), 2 );
614 myFirstIteration = false;
615 }
616
617 if ( mHistoMin < mHistoMax )
618 {
619 // for x axis use band pixel values rather than gdal hist. bin values
620 // subtract -0.5 to prevent rounding errors
621 // see http://www.gdal.org/classGDALRasterBand.html#3f8889607d3b2294f7e0f11181c201c8
622 // fix x range for non-Byte data
623 mpPlot->setAxisScale( QwtPlot::xBottom,
624 mHistoMin - myBinXStep / 2,
625 mHistoMax + myBinXStep / 2 );
626 mpPlot->setEnabled( true );
627 mpPlot->replot();
628
629 // histo plot markers
630 // memory leak?
631 mHistoMarkerMin = new QwtPlotMarker();
632 mHistoMarkerMin->attach( mpPlot );
633 mHistoMarkerMax = new QwtPlotMarker();
634 mHistoMarkerMax->attach( mpPlot );
635 updateHistoMarkers();
636
637 // histo picker
638 if ( !mHistoPicker )
639 {
640 mHistoPicker = new QwtPlotPicker( mpPlot->canvas() );
641 // mHistoPicker->setTrackerMode( QwtPicker::ActiveOnly );
642 mHistoPicker->setTrackerMode( QwtPicker::AlwaysOff );
643 mHistoPicker->setRubberBand( QwtPicker::VLineRubberBand );
644 mHistoPicker->setStateMachine( new QwtPickerDragPointMachine );
645 connect( mHistoPicker, static_cast < void ( QwtPlotPicker::* )( const QPointF & ) > ( &QwtPlotPicker::selected ), this, &QgsRasterHistogramWidget::histoPickerSelected );
646 }
647 mHistoPicker->setEnabled( false );
648
649 // plot zoomer
650 if ( !mHistoZoomer )
651 {
652 mHistoZoomer = new QwtPlotZoomer( mpPlot->canvas() );
653 mHistoZoomer->setStateMachine( new QwtPickerDragRectMachine );
654 mHistoZoomer->setTrackerMode( QwtPicker::AlwaysOff );
655 }
656 mHistoZoomer->setEnabled( true );
657 }
658 else
659 {
660 mpPlot->setDisabled( true );
661 if ( mHistoPicker )
662 mHistoPicker->setEnabled( false );
663 if ( mHistoZoomer )
664 mHistoZoomer->setEnabled( false );
665 }
666
667 stackedWidget2->setCurrentIndex( 0 );
668 // icon from http://findicons.com/icon/169577/14_zoom?id=171427
669 mpPlot->canvas()->setCursor( QCursor( QgsApplication::getThemePixmap( QStringLiteral( "/mIconZoom.svg" ) ) ) );
670 // cboHistoBand_currentIndexChanged( -1 );
671 QApplication::restoreOverrideCursor();
672}
673
674void QgsRasterHistogramWidget::mSaveAsImageButton_clicked()
675{
676 if ( !mpPlot )
677 return;
678
679 const QPair< QString, QString> myFileNameAndFilter = QgsGuiUtils::getSaveAsImageName( this, tr( "Choose a file name to save the map image as" ) );
680 const QFileInfo myInfo( myFileNameAndFilter.first );
681 if ( !myInfo.baseName().isEmpty() )
682 {
683 histoSaveAsImage( myFileNameAndFilter.first );
684 }
685}
686
687bool QgsRasterHistogramWidget::histoSaveAsImage( const QString &filename,
688 int width, int height, int quality )
689{
690 // make sure dir. exists
691 const QFileInfo myInfo( filename );
692 const QDir myDir( myInfo.dir() );
693 if ( ! myDir.exists() )
694 {
695 QgsDebugError( QStringLiteral( "Error, directory %1 non-existent (theFilename = %2)" ).arg( myDir.absolutePath(), filename ) );
696 return false;
697 }
698
699 // prepare the pixmap
700 QPixmap myPixmap( width, height );
701 const QRect myQRect( 5, 5, width - 10, height - 10 ); // leave a 5px border on all sides
702 myPixmap.fill( Qt::white ); // Qt::transparent ?
703
704 QwtPlotRenderer myRenderer;
705 myRenderer.setDiscardFlags( QwtPlotRenderer::DiscardBackground |
706 QwtPlotRenderer::DiscardCanvasBackground );
707 myRenderer.setLayoutFlags( QwtPlotRenderer::FrameWithScales );
708
709 QPainter myPainter;
710 myPainter.begin( &myPixmap );
711 myRenderer.render( mpPlot, &myPainter, myQRect );
712 myPainter.end();
713
714 // save pixmap to file
715 myPixmap.save( filename, nullptr, quality );
716
717 // should do more error checking
718 return true;
719}
720
722{
723 cboHistoBand->setCurrentIndex( bandNo - 1 );
724}
725
726void QgsRasterHistogramWidget::cboHistoBand_currentIndexChanged( int index )
727{
728 if ( mHistoShowBands == ShowSelected )
730
731 // get the current index value, index can be -1
732 index = cboHistoBand->currentIndex();
733 if ( mHistoPicker )
734 {
735 mHistoPicker->setEnabled( false );
736 mHistoPicker->setRubberBandPen( QPen( mHistoColors.at( index + 1 ) ) );
737 }
738 if ( mHistoZoomer )
739 mHistoZoomer->setEnabled( true );
740 btnHistoMin->setEnabled( true );
741 btnHistoMax->setEnabled( true );
742
743 const QPair< QString, QString > myMinMax = rendererMinMax( index + 1 );
744 leHistoMin->setText( myMinMax.first );
745 leHistoMax->setText( myMinMax.second );
746
747 applyHistoMin();
748 applyHistoMax();
749}
750
751void QgsRasterHistogramWidget::histoActionTriggered( QAction *action )
752{
753 if ( ! action )
754 return;
755 histoAction( action->data().toString(), action->isChecked() );
756}
757
758void QgsRasterHistogramWidget::histoAction( const QString &actionName, bool actionFlag )
759{
760 if ( actionName.isEmpty() )
761 return;
762
763 // this approach is a bit of a hack, but this way we don't have to define slots for each action
764 QgsDebugMsgLevel( QStringLiteral( "band = %1 action = %2" ).arg( cboHistoBand->currentIndex() + 1 ).arg( actionName ), 2 );
765
766 // checkeable actions
767 if ( actionName == QLatin1String( "Show markers" ) )
768 {
769 mHistoShowMarkers = actionFlag;
770 QgsSettings settings;
771 settings.setValue( QStringLiteral( "Raster/histogram/showMarkers" ), mHistoShowMarkers );
772 updateHistoMarkers();
773 return;
774 }
775 else if ( actionName == QLatin1String( "Zoom min_max" ) )
776 {
777 mHistoZoomToMinMax = actionFlag;
778 QgsSettings settings;
779 settings.setValue( QStringLiteral( "Raster/histogram/zoomToMinMax" ), mHistoZoomToMinMax );
780 return;
781 }
782 else if ( actionName == QLatin1String( "Update min_max" ) )
783 {
784 mHistoUpdateStyleToMinMax = actionFlag;
785 QgsSettings settings;
786 settings.setValue( QStringLiteral( "Raster/histogram/updateStyleToMinMax" ), mHistoUpdateStyleToMinMax );
787 return;
788 }
789 else if ( actionName == QLatin1String( "Show all" ) )
790 {
791 mHistoShowBands = ShowAll;
792 // settings.setValue( "/Raster/histogram/showBands", static_cast<int>(mHistoShowBands) );
794 return;
795 }
796 else if ( actionName == QLatin1String( "Show selected" ) )
797 {
798 mHistoShowBands = ShowSelected;
799 // settings.setValue( "/Raster/histogram/showBands", static_cast<int>(mHistoShowBands) );
801 return;
802 }
803 else if ( actionName == QLatin1String( "Show RGB" ) )
804 {
805 mHistoShowBands = ShowRGB;
806 // settings.setValue( "/Raster/histogram/showBands", static_cast<int>(mHistoShowBands) );
808 return;
809 }
810 else if ( actionName == QLatin1String( "Draw lines" ) )
811 {
812 mHistoDrawLines = actionFlag;
813 QgsSettings settings;
814 settings.setValue( QStringLiteral( "Raster/histogram/drawLines" ), mHistoDrawLines );
815 btnHistoCompute_clicked(); // refresh
816 return;
817 }
818#if 0
819 else if ( actionName == "Load apply all" )
820 {
821 mHistoLoadApplyAll = actionFlag;
822 settings.setValue( "/Raster/histogram/loadApplyAll", mHistoLoadApplyAll );
823 return;
824 }
825#endif
826 // Load actions
827 // TODO - separate calculations from rendererwidget so we can do them without
828 else if ( actionName.left( 5 ) == QLatin1String( "Load " ) && mRendererWidget )
829 {
830 QVector<int> myBands;
831 bool ok = false;
832
833#if 0
834 double minMaxValues[2];
835
836 // find which band(s) need updating (all or current)
837 if ( mHistoLoadApplyAll )
838 {
839 int myBandCountInt = mRasterLayer->bandCount();
840 for ( int i = 1; i <= myBandCountInt; i++ )
841 {
842 if ( i != cboHistoBand->currentIndex() + 1 )
843 myBands << i;
844 }
845 }
846#endif
847
848 // add current band to the end
849 myBands << cboHistoBand->currentIndex() + 1;
850
851 // get stddev value once if needed
852#if 0
853 double myStdDev = 1.0;
854 if ( actionName == "Load stddev" )
855 {
856 myStdDev = mRendererWidget->stdDev().toDouble();
857 }
858#endif
859
860 // don't update markers every time
861 leHistoMin->blockSignals( true );
862 leHistoMax->blockSignals( true );
863
864 // process each band
865 const auto constMyBands = myBands;
866 for ( const int bandNo : constMyBands )
867 {
868 ok = false;
869#if 0
870 if ( actionName == "Load actual" )
871 {
872 ok = mRendererWidget->bandMinMax( QgsRasterRendererWidget::Actual,
873 bandNo, minMaxValues );
874 }
875 else if ( actionName == "Load estimate" )
876 {
877 ok = mRendererWidget->bandMinMax( QgsRasterRendererWidget::Estimate,
878 bandNo, minMaxValues );
879 }
880 else if ( actionName == "Load extent" )
881 {
882 ok = mRendererWidget->bandMinMax( QgsRasterRendererWidget::CurrentExtent,
883 bandNo, minMaxValues );
884 }
885 else if ( actionName == "Load 1 stddev" ||
886 actionName == "Load stddev" )
887 {
888 ok = mRendererWidget->bandMinMaxFromStdDev( myStdDev, bandNo, minMaxValues );
889 }
890#endif
891
892 // apply current item
893 cboHistoBand->setCurrentIndex( bandNo - 1 );
894 if ( !ok || actionName == QLatin1String( "Load reset" ) )
895 {
896 leHistoMin->clear();
897 leHistoMax->clear();
898#if 0
899 // TODO - fix gdal provider: changes data type when nodata value is not found
900 // this prevents us from getting proper min and max values here
902 ( Qgis::DataType ) mRasterLayer->dataProvider()->dataType( bandNo ) );
904 ( Qgis::DataType ) mRasterLayer->dataProvider()->dataType( bandNo ) );
905 }
906 else
907 {
908 leHistoMin->setText( QString::number( minMaxValues[0] ) );
909 leHistoMax->setText( QString::number( minMaxValues[1] ) );
910#endif
911 }
912 applyHistoMin();
913 applyHistoMax();
914 }
915 // update markers
916 leHistoMin->blockSignals( false );
917 leHistoMax->blockSignals( false );
918 updateHistoMarkers();
919 }
920 else if ( actionName == QLatin1String( "Compute histogram" ) )
921 {
922 btnHistoCompute_clicked();
923 }
924 else
925 {
926 QgsDebugError( "Invalid action " + actionName );
927 return;
928 }
929}
930
931void QgsRasterHistogramWidget::applyHistoMin()
932{
933 if ( ! mRendererWidget )
934 return;
935
936 const int bandNo = cboHistoBand->currentIndex() + 1;
937 const QList< int > selectedBands = rendererSelectedBands();
938 QString min;
939 for ( int i = 0; i <= selectedBands.size(); i++ )
940 {
941 if ( bandNo == mRendererWidget->selectedBand( i ) )
942 {
943 min = leHistoMin->text();
944 if ( mHistoUpdateStyleToMinMax && mRendererWidget->min( i ) != min )
945 {
946 mRendererWidget->setMin( min, i );
948 {
950 }
951 if ( mRendererWidget->minMaxWidget() )
952 {
953 mRendererWidget->minMaxWidget()->userHasSetManualMinMaxValues();
954 }
955 }
956 }
957 }
958
959 updateHistoMarkers();
960
961 if ( ! min.isEmpty() && mHistoZoomToMinMax && mHistoZoomer )
962 {
963 QRectF rect = mHistoZoomer->zoomRect();
964 rect.setLeft( min.toDouble() );
965 mHistoZoomer->zoom( rect );
966 }
967 emit widgetChanged();
968}
969
970void QgsRasterHistogramWidget::applyHistoMax()
971{
972 if ( ! mRendererWidget )
973 return;
974
975 const int bandNo = cboHistoBand->currentIndex() + 1;
976 const QList< int > mySelectedBands = rendererSelectedBands();
977 QString max;
978 for ( int i = 0; i <= mySelectedBands.size(); i++ )
979 {
980 if ( bandNo == mRendererWidget->selectedBand( i ) )
981 {
982 max = leHistoMax->text();
983 if ( mHistoUpdateStyleToMinMax && mRendererWidget->max( i ) != max )
984 {
985 mRendererWidget->setMax( max, i );
987 {
989 }
990 if ( mRendererWidget->minMaxWidget() )
991 {
992 mRendererWidget->minMaxWidget()->userHasSetManualMinMaxValues();
993 }
994 }
995 }
996 }
997
998 updateHistoMarkers();
999
1000 if ( ! max.isEmpty() && mHistoZoomToMinMax && mHistoZoomer )
1001 {
1002 QRectF rect = mHistoZoomer->zoomRect();
1003 rect.setRight( max.toDouble() );
1004 mHistoZoomer->zoom( rect );
1005 }
1006 emit widgetChanged();
1007}
1008
1009void QgsRasterHistogramWidget::btnHistoMin_toggled()
1010{
1011 if ( mpPlot && mHistoPicker )
1012 {
1013 if ( QApplication::overrideCursor() )
1014 QApplication::restoreOverrideCursor();
1015 if ( btnHistoMin->isChecked() )
1016 {
1017 btnHistoMax->setChecked( false );
1018 QApplication::setOverrideCursor( Qt::PointingHandCursor );
1019 }
1020 if ( mHistoZoomer )
1021 mHistoZoomer->setEnabled( ! btnHistoMin->isChecked() );
1022 mHistoPicker->setEnabled( btnHistoMin->isChecked() );
1023 }
1024 updateHistoMarkers();
1025}
1026
1027void QgsRasterHistogramWidget::btnHistoMax_toggled()
1028{
1029 if ( mpPlot && mHistoPicker )
1030 {
1031 if ( QApplication::overrideCursor() )
1032 QApplication::restoreOverrideCursor();
1033 if ( btnHistoMax->isChecked() )
1034 {
1035 btnHistoMin->setChecked( false );
1036 QApplication::setOverrideCursor( Qt::PointingHandCursor );
1037 }
1038 if ( mHistoZoomer )
1039 mHistoZoomer->setEnabled( ! btnHistoMax->isChecked() );
1040 mHistoPicker->setEnabled( btnHistoMax->isChecked() );
1041 }
1042 updateHistoMarkers();
1043}
1044
1045// local function used by histoPickerSelected(), to get a rounded picked value
1046// this is sensitive and may not always be correct, needs more testing
1047QString findClosestTickVal( double target, const QwtScaleDiv *scale, int div = 100 )
1048{
1049 if ( !scale ) return QString();
1050
1051 QList< double > minorTicks = scale->ticks( QwtScaleDiv::MinorTick );
1052 QList< double > majorTicks = scale->ticks( QwtScaleDiv::MajorTick );
1053 const double diff = ( minorTicks[1] - minorTicks[0] ) / div;
1054 double min = majorTicks[0] - diff;
1055 if ( min > target )
1056 min -= ( majorTicks[1] - majorTicks[0] );
1057 const double max = scale->upperBound();
1058 double closest = target;
1059 double current = min;
1060
1061 while ( current < max )
1062 {
1063 current += diff;
1064 if ( current > target )
1065 {
1066 closest = ( std::fabs( target - current + diff ) < std::fabs( target - current ) ) ? current - diff : current;
1067 break;
1068 }
1069 }
1070
1071 // QgsDebugMsgLevel( QStringLiteral( "target=%1 div=%2 closest=%3" ).arg( target ).arg( div ).arg( closest ), 2 );
1072 return QLocale().toString( closest );
1073}
1074
1075void QgsRasterHistogramWidget::histoPickerSelected( QPointF pos )
1076{
1077 if ( btnHistoMin->isChecked() || btnHistoMax->isChecked() )
1078 {
1079 const QwtScaleDiv *scale = &mpPlot->axisScaleDiv( QwtPlot::xBottom );
1080
1081 if ( btnHistoMin->isChecked() )
1082 {
1083 leHistoMin->setText( findClosestTickVal( pos.x(), scale ) );
1084 applyHistoMin();
1085 btnHistoMin->setChecked( false );
1086 }
1087 else // if ( btnHistoMax->isChecked() )
1088 {
1089 leHistoMax->setText( findClosestTickVal( pos.x(), scale ) );
1090 applyHistoMax();
1091 btnHistoMax->setChecked( false );
1092 }
1093 }
1094 if ( QApplication::overrideCursor() )
1095 QApplication::restoreOverrideCursor();
1096}
1097
1098void QgsRasterHistogramWidget::histoPickerSelectedQwt5( QwtDoublePoint pos )
1099{
1100 histoPickerSelected( QPointF( pos.x(), pos.y() ) );
1101}
1102
1103void QgsRasterHistogramWidget::updateHistoMarkers()
1104{
1105 // hack to not update markers
1106 if ( leHistoMin->signalsBlocked() )
1107 return;
1108 // todo error checking
1109 if ( !mpPlot || !mHistoMarkerMin || !mHistoMarkerMax )
1110 return;
1111
1112 const int bandNo = cboHistoBand->currentIndex() + 1;
1113 const QList< int > mySelectedBands = histoSelectedBands();
1114
1115 if ( ( ! mHistoShowMarkers && ! btnHistoMin->isChecked() && ! btnHistoMax->isChecked() ) ||
1116 ( ! mySelectedBands.isEmpty() && ! mySelectedBands.contains( bandNo ) ) )
1117 {
1118 mHistoMarkerMin->hide();
1119 mHistoMarkerMax->hide();
1120 mpPlot->replot();
1121 return;
1122 }
1123
1124 double minVal = mHistoMin;
1125 double maxVal = mHistoMax;
1126 const QString minStr = leHistoMin->text();
1127 const QString maxStr = leHistoMax->text();
1128 if ( !minStr.isEmpty() )
1129 minVal = minStr.toDouble();
1130 if ( !maxStr.isEmpty() )
1131 maxVal = maxStr.toDouble();
1132
1133 QPen linePen = QPen( mHistoColors.at( bandNo ) );
1134 linePen.setStyle( Qt::DashLine );
1135 mHistoMarkerMin->setLineStyle( QwtPlotMarker::VLine );
1136 mHistoMarkerMin->setLinePen( linePen );
1137 mHistoMarkerMin->setXValue( minVal );
1138 mHistoMarkerMin->show();
1139 mHistoMarkerMax->setLineStyle( QwtPlotMarker::VLine );
1140 mHistoMarkerMax->setLinePen( linePen );
1141 mHistoMarkerMax->setXValue( maxVal );
1142 mHistoMarkerMax->show();
1143
1144 mpPlot->replot();
1145}
1146
1147
1148QList< int > QgsRasterHistogramWidget::histoSelectedBands()
1149{
1150 QList< int > mySelectedBands;
1151
1152 if ( mHistoShowBands != ShowAll )
1153 {
1154 if ( mHistoShowBands == ShowSelected )
1155 {
1156 mySelectedBands << cboHistoBand->currentIndex() + 1;
1157 }
1158 else if ( mHistoShowBands == ShowRGB )
1159 {
1160 mySelectedBands = rendererSelectedBands();
1161 }
1162 }
1163
1164 return mySelectedBands;
1165}
1166
1167QList< int > QgsRasterHistogramWidget::rendererSelectedBands()
1168{
1169 QList< int > mySelectedBands;
1170
1171 if ( ! mRendererWidget )
1172 {
1173 mySelectedBands << -1 << -1 << -1; // make sure we return 3 elements
1174 return mySelectedBands;
1175 }
1176
1177 if ( mRendererName == QLatin1String( "singlebandgray" ) ||
1178 mRendererName == QLatin1String( "singlebandpseudocolor" ) )
1179 {
1180 mySelectedBands << mRendererWidget->selectedBand();
1181 }
1182 else if ( mRendererName == QLatin1String( "multibandcolor" ) )
1183 {
1184 for ( int i = 0; i <= 2; i++ )
1185 {
1186 mySelectedBands << mRendererWidget->selectedBand( i );
1187 }
1188 }
1189
1190 return mySelectedBands;
1191}
1192
1193QPair< QString, QString > QgsRasterHistogramWidget::rendererMinMax( int bandNo )
1194{
1195 QPair< QString, QString > myMinMax;
1196
1197 if ( ! mRendererWidget )
1198 return myMinMax;
1199
1200 if ( mRendererName == QLatin1String( "singlebandgray" ) ||
1201 mRendererName == QLatin1String( "singlebandpseudocolor" ) )
1202 {
1203 if ( bandNo == mRendererWidget->selectedBand() )
1204 {
1205 myMinMax.first = mRendererWidget->min();
1206 myMinMax.second = mRendererWidget->max();
1207 }
1208 }
1209 else if ( mRendererName == QLatin1String( "multibandcolor" ) )
1210 {
1211 for ( int i = 0; i <= 2; i++ )
1212 {
1213 if ( bandNo == mRendererWidget->selectedBand( i ) )
1214 {
1215 myMinMax.first = mRendererWidget->min( i );
1216 myMinMax.second = mRendererWidget->max( i );
1217 break;
1218 }
1219 }
1220 }
1221
1222 // TODO - there are 2 definitions of raster data type that should be unified
1223 // QgsRasterDataProvider::DataType and Qgis::DataType
1224 // TODO - fix gdal provider: changes data type when nodata value is not found
1225 // this prevents us from getting proper min and max values here
1226 // minStr = QString::number( QgsContrastEnhancement::minimumValuePossible( ( Qgis::DataType )
1227 // mRasterLayer->dataProvider()->dataType( bandNo ) ) );
1228 // maxStr = QString::number( QgsContrastEnhancement::maximumValuePossible( ( Qgis::DataType )
1229 // mRasterLayer->dataProvider()->dataType( bandNo ) ) );
1230
1231 // if we get an empty result, fill with default value (histo min/max)
1232 if ( myMinMax.first.isEmpty() )
1233 myMinMax.first = QLocale().toString( mHistoMin );
1234 if ( myMinMax.second.isEmpty() )
1235 myMinMax.second = QLocale().toString( mHistoMax );
1236
1237 QgsDebugMsgLevel( QStringLiteral( "bandNo %1 got min/max [%2] [%3]" ).arg( bandNo ).arg( myMinMax.first, myMinMax.second ), 2 );
1238
1239 return myMinMax;
1240}
1241
1243{
1244
1245}
@ Size
Original data source size (and thus resolution) is known, it is not always available,...
DataType
Raster data types.
Definition qgis.h:351
@ Int16
Sixteen bit signed integer (qint16)
@ Int8
Eight bit signed integer (qint8) (added in QGIS 3.30)
@ UInt16
Sixteen bit unsigned integer (quint16)
@ Byte
Eight bit unsigned integer (quint8)
@ Int32
Thirty two bit signed integer (qint32)
@ UInt32
Thirty two bit unsigned integer (quint32)
static QPixmap getThemePixmap(const QString &name, const QColor &foreColor=QColor(), const QColor &backColor=QColor(), int size=16)
Helper to get a theme icon as a pixmap.
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
@ StretchToMinimumMaximum
Linear histogram.
@ NoEnhancement
Default color scaling algorithm, no scaling is applied.
static double maximumValuePossible(Qgis::DataType dataType)
Helper function that returns the maximum possible value for a data type.
static double minimumValuePossible(Qgis::DataType dataType)
Helper function that returns the minimum possible value for a data type.
QgsDoubleValidator is a QLineEdit Validator that combines QDoubleValidator and QRegularExpressionVali...
void progressChanged(double progress)
Emitted when the feedback object reports a progress change.
A panel widget that can be shown in the map style dock.
void widgetChanged()
Emitted when the widget state changes.
double minimumValue
The minimum cell value in the raster band.
double maximumValue
The maximum cell value in the raster band.
Feedback object tailored for raster block reading.
Qgis::DataType sourceDataType(int bandNo) const override=0
Returns source data type for the band specified by number, source data type may be shorter than dataT...
Qgis::DataType dataType(int bandNo) const override=0
Returns data type for the band specified by number.
void setRendererWidget(const QString &name, QgsRasterRendererWidget *rendererWidget=nullptr)
Sets the renderer widget (or just its name if there is no widget)
void setSelectedBand(int index)
Apply a histoActionTriggered() event.
QgsRasterHistogramWidget(QgsRasterLayer *layer, QWidget *parent=nullptr)
Constructor for QgsRasterHistogramWidget, for the specified raster layer.
void refreshHistogram()
slot executed when user wishes to refresh raster histogramwidget
void histoAction(const QString &actionName, bool actionFlag=true)
Apply a histoActionTriggered() event.
void setActive(bool activeFlag)
Activate the histogram widget.
bool computeHistogram(bool forceComputeFlag)
Compute the histogram on demand.
bool histoSaveAsImage(const QString &filename, int width=600, int height=600, int quality=-1)
Save the histogram as an image to disk.
The QgsRasterHistogram is a container for histogram of a single raster band.
double minimum
The minimum histogram value.
double maximum
The maximum histogram value.
QgsRasterHistogram::HistogramVector histogramVector
Stores the histogram for a given layer.
int binCount
Number of bins (intervals,buckets) in histogram.
Base class for processing filters like renderers, reprojector, resampler etc.
virtual Qgis::RasterInterfaceCapabilities capabilities() const
Returns the capabilities supported by the interface.
virtual int xSize() const
Gets raster size.
Q_DECL_DEPRECATED QgsRasterBandStats bandStatistics(int bandNo, int stats, const QgsRectangle &extent=QgsRectangle(), int sampleSize=0, QgsRasterBlockFeedback *feedback=nullptr)
Returns the band statistics.
virtual Qgis::DataType sourceDataType(int bandNo) const
Returns source data type for the band specified by number, source data type may be shorter than dataT...
virtual bool hasHistogram(int bandNo, int binCount, double minimum=std::numeric_limits< double >::quiet_NaN(), double maximum=std::numeric_limits< double >::quiet_NaN(), const QgsRectangle &extent=QgsRectangle(), int sampleSize=0, bool includeOutOfRange=false)
Returns true if histogram is available (cached, already calculated)
virtual int ySize() const
virtual QgsRectangle extent() const
Gets the extent of the interface.
virtual QgsRasterHistogram histogram(int bandNo, int binCount=0, double minimum=std::numeric_limits< double >::quiet_NaN(), double maximum=std::numeric_limits< double >::quiet_NaN(), const QgsRectangle &extent=QgsRectangle(), int sampleSize=0, bool includeOutOfRange=false, QgsRasterBlockFeedback *feedback=nullptr)
Returns a band histogram.
Represents a raster layer.
int bandCount() const
Returns the number of bands in this layer.
QString bandName(int bandNoInt) const
Returns the name of a band given its number.
QgsRasterDataProvider * dataProvider() override
Returns the source data provider.
void userHasSetManualMinMaxValues()
Uncheck cumulative cut, min/max, std-dev radio buttons.
Abstract base class for widgets which configure a QgsRasterRenderer.
virtual QgsRasterMinMaxWidget * minMaxWidget()
Returns min/max widget when it exists.
virtual int selectedBand(int index=0)
virtual void setMax(const QString &value, int index=0)
virtual QString max(int index=0)
virtual void setContrastEnhancementAlgorithm(QgsContrastEnhancement::ContrastEnhancementAlgorithm algorithm)
Sets the contrast enhancement algorithm to be used by the raster renderer.
virtual void setMin(const QString &value, int index=0)
virtual QString min(int index=0)
virtual QgsContrastEnhancement::ContrastEnhancementAlgorithm contrastEnhancementAlgorithm() const
Returns the contrast enhancement algorithm to be used by the raster renderer.
A rectangle specified with double values.
This class is a composition of two QSettings instances:
Definition qgssettings.h:64
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
QPair< QString, QString > GUI_EXPORT getSaveAsImageName(QWidget *parent, const QString &message, const QString &defaultFilename)
A helper function to get an image name from the user.
QPointF QwtDoublePoint
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:39
#define QgsDebugError(str)
Definition qgslogger.h:38
constexpr int SAMPLE_SIZE
QString findClosestTickVal(double target, const QwtScaleDiv *scale, int div=100)