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