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