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