QGIS API Documentation 3.99.0-Master (d270888f95f)
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;
102 myIteratorInt <= myBandCountInt;
103 ++myIteratorInt )
104 {
105 cboHistoBand->addItem( mRasterLayer->bandName( myIteratorInt ) );
106 const Qgis::DataType mySrcDataType = mRasterLayer->dataProvider()->sourceDataType( myIteratorInt );
107 if ( !( mySrcDataType == Qgis::DataType::Byte || mySrcDataType == Qgis::DataType::Int8 || mySrcDataType == Qgis::DataType::Int16 || mySrcDataType == Qgis::DataType::Int32 || mySrcDataType == Qgis::DataType::UInt16 || mySrcDataType == Qgis::DataType::UInt32 ) )
108 isInt = false;
109 }
110
111 // histo min/max selectors
112 leHistoMin->setValidator( new QgsDoubleValidator( this ) );
113 leHistoMax->setValidator( new QgsDoubleValidator( this ) );
114
115 connect( leHistoMin, &QLineEdit::editingFinished, this, &QgsRasterHistogramWidget::applyHistoMin );
116 connect( leHistoMax, &QLineEdit::editingFinished, this, &QgsRasterHistogramWidget::applyHistoMax );
117
118 // histo actions
119 // TODO move/add options to qgis options dialog
120 QMenu *menu = new QMenu( this );
121 menu->setSeparatorsCollapsible( false );
122 btnHistoActions->setMenu( menu );
123 QActionGroup *group = nullptr;
124 QAction *action = nullptr;
125
126 // min/max options
127 group = new QActionGroup( this );
128 group->setExclusive( false );
129 connect( group, &QActionGroup::triggered, this, &QgsRasterHistogramWidget::histoActionTriggered );
130 action = new QAction( tr( "Min/Max options" ), group );
131 action->setSeparator( true );
132 menu->addAction( action );
133 action = new QAction( tr( "Always show min/max markers" ), group );
134 action->setData( QVariant( "Show markers" ) );
135 action->setCheckable( true );
136 action->setChecked( mHistoShowMarkers );
137 menu->addAction( action );
138 action = new QAction( tr( "Zoom to min/max" ), group );
139 action->setData( QVariant( "Zoom min_max" ) );
140 action->setCheckable( true );
141 action->setChecked( mHistoZoomToMinMax );
142 menu->addAction( action );
143 action = new QAction( tr( "Update style to min/max" ), group );
144 action->setData( QVariant( "Update min_max" ) );
145 action->setCheckable( true );
146 action->setChecked( mHistoUpdateStyleToMinMax );
147 menu->addAction( action );
148
149 // visibility options
150 group = new QActionGroup( this );
151 group->setExclusive( false );
152 connect( group, &QActionGroup::triggered, this, &QgsRasterHistogramWidget::histoActionTriggered );
153 action = new QAction( tr( "Visibility" ), group );
154 action->setSeparator( true );
155 menu->addAction( action );
156 group = new QActionGroup( this );
157 group->setExclusive( true ); // these options are exclusive
158 connect( group, &QActionGroup::triggered, this, &QgsRasterHistogramWidget::histoActionTriggered );
159 action = new QAction( tr( "Show all bands" ), group );
160 action->setData( QVariant( "Show all" ) );
161 action->setCheckable( true );
162 action->setChecked( mHistoShowBands == ShowAll );
163 menu->addAction( action );
164 action = new QAction( tr( "Show RGB/Gray band(s)" ), group );
165 action->setData( QVariant( "Show RGB" ) );
166 action->setCheckable( true );
167 action->setChecked( mHistoShowBands == ShowRGB );
168 menu->addAction( action );
169 action = new QAction( tr( "Show selected band" ), group );
170 action->setData( QVariant( "Show selected" ) );
171 action->setCheckable( true );
172 action->setChecked( mHistoShowBands == ShowSelected );
173 menu->addAction( action );
174
175 // display options
176 group = new QActionGroup( this );
177 group->setExclusive( false );
178 connect( group, &QActionGroup::triggered, this, &QgsRasterHistogramWidget::histoActionTriggered );
179 action = new QAction( tr( "Display" ), group );
180 action->setSeparator( true );
181 menu->addAction( action );
182 // should we plot as histogram instead of line plot? (int data only)
183 action = new QAction( QString(), group );
184 action->setData( QVariant( "Draw lines" ) );
185 if ( isInt )
186 {
187 action->setText( tr( "Draw as lines" ) );
188 action->setCheckable( true );
189 action->setChecked( mHistoDrawLines );
190 }
191 else
192 {
193 action->setText( tr( "Draw as lines (only int layers)" ) );
194 action->setEnabled( false );
195 }
196 menu->addAction( action );
197
198 // actions
199 action = new QAction( tr( "Actions" ), group );
200 action->setSeparator( true );
201 menu->addAction( action );
202
203 // load actions
204 group = new QActionGroup( this );
205 group->setExclusive( false );
206 connect( group, &QActionGroup::triggered, this, &QgsRasterHistogramWidget::histoActionTriggered );
207 action = new QAction( tr( "Reset" ), group );
208 action->setData( QVariant( "Load reset" ) );
209 menu->addAction( action );
210
211 // these actions have been disabled for api cleanup, restore them eventually
212 // TODO restore these in qgis 2.4
213#if 0
214 // Load min/max needs 3 params (method, extent, accuracy), cannot put it in single item
215 action = new QAction( tr( "Load min/max" ), group );
216 action->setSeparator( true );
217 menu->addAction( action );
218 action = new QAction( tr( "Estimate (faster)" ), group );
219 action->setData( QVariant( "Load estimate" ) );
220 menu->addAction( action );
221 action = new QAction( tr( "Actual (slower)" ), group );
222 action->setData( QVariant( "Load actual" ) );
223 menu->addAction( action );
224 action = new QAction( tr( "Current extent" ), group );
225 action->setData( QVariant( "Load extent" ) );
226 menu->addAction( action );
227 action = new QAction( tr( "Use stddev (1.0)" ), group );
228 action->setData( QVariant( "Load 1 stddev" ) );
229 menu->addAction( action );
230 action = new QAction( tr( "Use stddev (custom)" ), group );
231 action->setData( QVariant( "Load stddev" ) );
232 menu->addAction( action );
233 action = new QAction( tr( "Load for each band" ), group );
234 action->setData( QVariant( "Load apply all" ) );
235 action->setCheckable( true );
236 action->setChecked( mHistoLoadApplyAll );
237 menu->addAction( action );
238#endif
239
240 //others
241 action = new QAction( tr( "Recompute Histogram" ), group );
242 action->setData( QVariant( "Compute histogram" ) );
243 menu->addAction( action );
244 }
245
246} // QgsRasterHistogramWidget ctor
247
249{
250 mRendererName = name;
251 mRendererWidget = rendererWidget;
253 cboHistoBand_currentIndexChanged( -1 );
254}
255
257{
258 if ( activeFlag )
259 {
261 cboHistoBand_currentIndexChanged( -1 );
262 }
263 else
264 {
265 if ( QApplication::overrideCursor() )
266 QApplication::restoreOverrideCursor();
267 btnHistoMin->setChecked( false );
268 btnHistoMax->setChecked( false );
269 }
270}
271
272void QgsRasterHistogramWidget::btnHistoCompute_clicked()
273{
274 // Histogram computation can be called either by clicking the "Compute Histogram" button
275 // which is only visible if there is no cached histogram or by calling the
276 // "Compute Histogram" action. Due to limitations in the gdal api, it is not possible
277 // to re-calculate the histogram if it has already been calculated
278 computeHistogram( true );
280}
281
282// Compute the number of bins
283// Logic partially borrowed to QgsRasterInterface::initHistogram(),
284// but with a limitation to 1000 bins. Otherwise the histogram will be
285// unreadable (see https://github.com/qgis/QGIS/issues/38298)
286// NOTE: the number of bins should probably be let to the user, and/or adaptative
287// to the width in pixels of the chart.
288static int getBinCount( QgsRasterInterface *rasterInterface, int bandNo, int sampleSize )
289{
290 const Qgis::DataType mySrcDataType = rasterInterface->sourceDataType( bandNo );
291 const double statsMin = mySrcDataType == Qgis::DataType::Byte ? 0 : rasterInterface->bandStatistics( bandNo, Qgis::RasterBandStatistic::Min, QgsRectangle(), sampleSize ).minimumValue;
292 const double statsMax = mySrcDataType == Qgis::DataType::Byte ? 255 : rasterInterface->bandStatistics( bandNo, Qgis::RasterBandStatistic::Max, QgsRectangle(), sampleSize ).maximumValue;
293 const QgsRectangle extent( rasterInterface->extent() );
294
295 // Calc resolution from sampleSize
296 double xRes, yRes;
297 xRes = yRes = std::sqrt( ( static_cast<double>( extent.width() ) * extent.height() ) / sampleSize );
298
299 // But limit by physical resolution
300 if ( rasterInterface->capabilities() & Qgis::RasterInterfaceCapability::Size )
301 {
302 const double srcXRes = extent.width() / rasterInterface->xSize();
303 const double srcYRes = extent.height() / rasterInterface->ySize();
304 if ( xRes < srcXRes )
305 xRes = srcXRes;
306 if ( yRes < srcYRes )
307 yRes = srcYRes;
308 }
309
310 const int histogramWidth = static_cast<int>( extent.width() / xRes );
311 const int histogramHeight = static_cast<int>( extent.height() / yRes );
312
313 int binCount = static_cast<int>( std::min( static_cast<qint64>( 1000 ), static_cast<qint64>( histogramWidth ) * histogramHeight ) );
314
315 if ( mySrcDataType == Qgis::DataType::Int16 || mySrcDataType == Qgis::DataType::Int32 || mySrcDataType == Qgis::DataType::UInt16 || mySrcDataType == Qgis::DataType::UInt32 )
316 {
317 binCount = static_cast<int>( std::min( static_cast<qint64>( binCount ), static_cast<qint64>( std::ceil( statsMax - statsMin + 1 ) ) ) );
318 }
319
320 return binCount;
321}
322
324{
325 //bool myIgnoreOutOfRangeFlag = true;
326 //bool myThoroughBandScanFlag = false;
327 const int myBandCountInt = mRasterLayer->bandCount();
328
329 // if forceComputeFlag = false make sure raster has cached histogram, else return false
330 if ( !forceComputeFlag )
331 {
332 for ( int myIteratorInt = 1;
333 myIteratorInt <= myBandCountInt;
334 ++myIteratorInt )
335 {
336 const int sampleSize = SAMPLE_SIZE; // number of sample cells
337 const int binCount = getBinCount( mRasterLayer->dataProvider(), myIteratorInt, sampleSize );
338 if ( !mRasterLayer->dataProvider()->hasHistogram( myIteratorInt, binCount, std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN(), QgsRectangle(), sampleSize ) )
339 {
340 QgsDebugMsgLevel( u"band %1 does not have cached histo"_s.arg( myIteratorInt ), 2 );
341 return false;
342 }
343 }
344 }
345
346 // compute histogram
347 stackedWidget2->setCurrentIndex( 1 );
348
349 const std::unique_ptr<QgsRasterBlockFeedback> feedback( new QgsRasterBlockFeedback() );
350 connect( feedback.get(), &QgsRasterBlockFeedback::progressChanged, mHistogramProgress, &QProgressBar::setValue );
351 QApplication::setOverrideCursor( Qt::WaitCursor );
352
353 for ( int myIteratorInt = 1;
354 myIteratorInt <= myBandCountInt;
355 ++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;
511 bandNumber <= myBandCountInt;
512 ++bandNumber )
513 {
514 /* skip this band if mHistoShowBands != ShowAll and this band is not selected */
515 if ( mHistoShowBands != ShowAll )
516 {
517 if ( !mySelectedBands.contains( bandNumber ) )
518 continue;
519 }
520
521 const int sampleSize = SAMPLE_SIZE; // number of sample cells
522
523 const std::unique_ptr<QgsRasterBlockFeedback> feedback( new QgsRasterBlockFeedback() );
524 connect( feedback.get(), &QgsRasterBlockFeedback::progressChanged, mHistogramProgress, &QProgressBar::setValue );
525
526 const int binCount = getBinCount( mRasterLayer->dataProvider(), bandNumber, sampleSize );
527 const QgsRasterHistogram myHistogram = mRasterLayer->dataProvider()->histogram( bandNumber, binCount, std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN(), QgsRectangle(), sampleSize, false, feedback.get() );
528
529 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 );
530
531 const Qgis::DataType mySrcDataType = mRasterLayer->dataProvider()->sourceDataType( bandNumber );
532 bool myDrawLines = true;
533 if ( !mHistoDrawLines && ( 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;
751 QgsSettings settings;
752 settings.setValue( u"Raster/histogram/showMarkers"_s, mHistoShowMarkers );
753 updateHistoMarkers();
754 return;
755 }
756 else if ( actionName == "Zoom min_max"_L1 )
757 {
758 mHistoZoomToMinMax = actionFlag;
759 QgsSettings settings;
760 settings.setValue( u"Raster/histogram/zoomToMinMax"_s, mHistoZoomToMinMax );
761 return;
762 }
763 else if ( actionName == "Update min_max"_L1 )
764 {
765 mHistoUpdateStyleToMinMax = actionFlag;
766 QgsSettings settings;
767 settings.setValue( u"Raster/histogram/updateStyleToMinMax"_s, mHistoUpdateStyleToMinMax );
768 return;
769 }
770 else if ( actionName == "Show all"_L1 )
771 {
772 mHistoShowBands = ShowAll;
773 // settings.setValue( "/Raster/histogram/showBands", static_cast<int>(mHistoShowBands) );
775 return;
776 }
777 else if ( actionName == "Show selected"_L1 )
778 {
779 mHistoShowBands = ShowSelected;
780 // settings.setValue( "/Raster/histogram/showBands", static_cast<int>(mHistoShowBands) );
782 return;
783 }
784 else if ( actionName == "Show RGB"_L1 )
785 {
786 mHistoShowBands = ShowRGB;
787 // settings.setValue( "/Raster/histogram/showBands", static_cast<int>(mHistoShowBands) );
789 return;
790 }
791 else if ( actionName == "Draw lines"_L1 )
792 {
793 mHistoDrawLines = actionFlag;
794 QgsSettings settings;
795 settings.setValue( u"Raster/histogram/drawLines"_s, mHistoDrawLines );
796 btnHistoCompute_clicked(); // refresh
797 return;
798 }
799#if 0
800 else if ( actionName == "Load apply all" )
801 {
802 mHistoLoadApplyAll = actionFlag;
803 settings.setValue( "/Raster/histogram/loadApplyAll", mHistoLoadApplyAll );
804 return;
805 }
806#endif
807 // Load actions
808 // TODO - separate calculations from rendererwidget so we can do them without
809 else if ( actionName.left( 5 ) == "Load "_L1 && mRendererWidget )
810 {
811 QVector<int> myBands;
812 bool ok = false;
813
814#if 0
815 double minMaxValues[2];
816
817 // find which band(s) need updating (all or current)
818 if ( mHistoLoadApplyAll )
819 {
820 int myBandCountInt = mRasterLayer->bandCount();
821 for ( int i = 1; i <= myBandCountInt; i++ )
822 {
823 if ( i != cboHistoBand->currentIndex() + 1 )
824 myBands << i;
825 }
826 }
827#endif
828
829 // add current band to the end
830 myBands << cboHistoBand->currentIndex() + 1;
831
832 // get stddev value once if needed
833#if 0
834 double myStdDev = 1.0;
835 if ( actionName == "Load stddev" )
836 {
837 myStdDev = mRendererWidget->stdDev().toDouble();
838 }
839#endif
840
841 // don't update markers every time
842 leHistoMin->blockSignals( true );
843 leHistoMax->blockSignals( true );
844
845 // process each band
846 const auto constMyBands = myBands;
847 for ( const int bandNo : constMyBands )
848 {
849 ok = false;
850#if 0
851 if ( actionName == "Load actual" )
852 {
853 ok = mRendererWidget->bandMinMax( QgsRasterRendererWidget::Actual,
854 bandNo, minMaxValues );
855 }
856 else if ( actionName == "Load estimate" )
857 {
858 ok = mRendererWidget->bandMinMax( QgsRasterRendererWidget::Estimate,
859 bandNo, minMaxValues );
860 }
861 else if ( actionName == "Load extent" )
862 {
863 ok = mRendererWidget->bandMinMax( QgsRasterRendererWidget::CurrentExtent,
864 bandNo, minMaxValues );
865 }
866 else if ( actionName == "Load 1 stddev" ||
867 actionName == "Load stddev" )
868 {
869 ok = mRendererWidget->bandMinMaxFromStdDev( myStdDev, bandNo, minMaxValues );
870 }
871#endif
872
873 // apply current item
874 cboHistoBand->setCurrentIndex( bandNo - 1 );
875 if ( !ok || actionName == "Load reset"_L1 )
876 {
877 leHistoMin->clear();
878 leHistoMax->clear();
879#if 0
880 // TODO - fix gdal provider: changes data type when nodata value is not found
881 // this prevents us from getting proper min and max values here
883 ( Qgis::DataType ) mRasterLayer->dataProvider()->dataType( bandNo ) );
885 ( Qgis::DataType ) mRasterLayer->dataProvider()->dataType( bandNo ) );
886 }
887 else
888 {
889 leHistoMin->setText( QString::number( minMaxValues[0] ) );
890 leHistoMax->setText( QString::number( minMaxValues[1] ) );
891#endif
892 }
893 applyHistoMin();
894 applyHistoMax();
895 }
896 // update markers
897 leHistoMin->blockSignals( false );
898 leHistoMax->blockSignals( false );
899 updateHistoMarkers();
900 }
901 else if ( actionName == "Compute histogram"_L1 )
902 {
903 btnHistoCompute_clicked();
904 }
905 else
906 {
907 QgsDebugError( "Invalid action " + actionName );
908 return;
909 }
910}
911
912void QgsRasterHistogramWidget::applyHistoMin()
913{
914 if ( !mRendererWidget )
915 return;
916
917 const int bandNo = cboHistoBand->currentIndex() + 1;
918 const QList<int> selectedBands = rendererSelectedBands();
919 QString min;
920 for ( int i = 0; i <= selectedBands.size(); i++ )
921 {
922 if ( bandNo == mRendererWidget->selectedBand( i ) )
923 {
924 min = leHistoMin->text();
925 if ( mHistoUpdateStyleToMinMax && mRendererWidget->min( i ) != min )
926 {
927 mRendererWidget->setMin( min, i );
929 {
931 }
932 if ( mRendererWidget->minMaxWidget() )
933 {
934 mRendererWidget->minMaxWidget()->userHasSetManualMinMaxValues();
935 }
936 }
937 }
938 }
939
940 updateHistoMarkers();
941
942 if ( !min.isEmpty() && mHistoZoomToMinMax && mHistoZoomer )
943 {
944 QRectF rect = mHistoZoomer->zoomRect();
945 rect.setLeft( min.toDouble() );
946 mHistoZoomer->zoom( rect );
947 }
948 emit widgetChanged();
949}
950
951void QgsRasterHistogramWidget::applyHistoMax()
952{
953 if ( !mRendererWidget )
954 return;
955
956 const int bandNo = cboHistoBand->currentIndex() + 1;
957 const QList<int> mySelectedBands = rendererSelectedBands();
958 QString max;
959 for ( int i = 0; i <= mySelectedBands.size(); i++ )
960 {
961 if ( bandNo == mRendererWidget->selectedBand( i ) )
962 {
963 max = leHistoMax->text();
964 if ( mHistoUpdateStyleToMinMax && mRendererWidget->max( i ) != max )
965 {
966 mRendererWidget->setMax( max, i );
967 if ( mRendererWidget->contrastEnhancementAlgorithm() == QgsContrastEnhancement::NoEnhancement )
968 {
969 mRendererWidget->setContrastEnhancementAlgorithm( QgsContrastEnhancement::StretchToMinimumMaximum );
970 }
971 if ( mRendererWidget->minMaxWidget() )
972 {
973 mRendererWidget->minMaxWidget()->userHasSetManualMinMaxValues();
974 }
975 }
976 }
977 }
978
979 updateHistoMarkers();
980
981 if ( !max.isEmpty() && mHistoZoomToMinMax && mHistoZoomer )
982 {
983 QRectF rect = mHistoZoomer->zoomRect();
984 rect.setRight( max.toDouble() );
985 mHistoZoomer->zoom( rect );
986 }
987 emit widgetChanged();
988}
989
990void QgsRasterHistogramWidget::btnHistoMin_toggled()
991{
992 if ( mpPlot && mHistoPicker )
993 {
994 if ( QApplication::overrideCursor() )
995 QApplication::restoreOverrideCursor();
996 if ( btnHistoMin->isChecked() )
997 {
998 btnHistoMax->setChecked( false );
999 QApplication::setOverrideCursor( Qt::PointingHandCursor );
1000 }
1001 if ( mHistoZoomer )
1002 mHistoZoomer->setEnabled( !btnHistoMin->isChecked() );
1003 mHistoPicker->setEnabled( btnHistoMin->isChecked() );
1004 }
1005 updateHistoMarkers();
1006}
1007
1008void QgsRasterHistogramWidget::btnHistoMax_toggled()
1009{
1010 if ( mpPlot && mHistoPicker )
1011 {
1012 if ( QApplication::overrideCursor() )
1013 QApplication::restoreOverrideCursor();
1014 if ( btnHistoMax->isChecked() )
1015 {
1016 btnHistoMin->setChecked( false );
1017 QApplication::setOverrideCursor( Qt::PointingHandCursor );
1018 }
1019 if ( mHistoZoomer )
1020 mHistoZoomer->setEnabled( !btnHistoMax->isChecked() );
1021 mHistoPicker->setEnabled( btnHistoMax->isChecked() );
1022 }
1023 updateHistoMarkers();
1024}
1025
1026// local function used by histoPickerSelected(), to get a rounded picked value
1027// this is sensitive and may not always be correct, needs more testing
1028QString findClosestTickVal( double target, const QwtScaleDiv *scale, int div = 100 )
1029{
1030 if ( !scale )
1031 return QString();
1032
1033 QList<double> minorTicks = scale->ticks( QwtScaleDiv::MinorTick );
1034 QList<double> majorTicks = scale->ticks( QwtScaleDiv::MajorTick );
1035 const double diff = ( minorTicks[1] - minorTicks[0] ) / div;
1036 double min = majorTicks[0] - diff;
1037 if ( min > target )
1038 min -= ( majorTicks[1] - majorTicks[0] );
1039 const double max = scale->upperBound();
1040 double closest = target;
1041 double current = min;
1042
1043 while ( current < max )
1044 {
1045 current += diff;
1046 if ( current > target )
1047 {
1048 closest = ( std::fabs( target - current + diff ) < std::fabs( target - current ) ) ? current - diff : current;
1049 break;
1050 }
1051 }
1052
1053 // QgsDebugMsgLevel( u"target=%1 div=%2 closest=%3"_s.arg( target ).arg( div ).arg( closest ), 2 );
1054 return QLocale().toString( closest );
1055}
1056
1057void QgsRasterHistogramWidget::histoPickerSelected( QPointF pos )
1058{
1059 if ( btnHistoMin->isChecked() || btnHistoMax->isChecked() )
1060 {
1061 const QwtScaleDiv *scale = &mpPlot->axisScaleDiv( QwtPlot::xBottom );
1062
1063 if ( btnHistoMin->isChecked() )
1064 {
1065 leHistoMin->setText( findClosestTickVal( pos.x(), scale ) );
1066 applyHistoMin();
1067 btnHistoMin->setChecked( false );
1068 }
1069 else // if ( btnHistoMax->isChecked() )
1070 {
1071 leHistoMax->setText( findClosestTickVal( pos.x(), scale ) );
1072 applyHistoMax();
1073 btnHistoMax->setChecked( false );
1074 }
1075 }
1076 if ( QApplication::overrideCursor() )
1077 QApplication::restoreOverrideCursor();
1078}
1079
1080void QgsRasterHistogramWidget::histoPickerSelectedQwt5( QwtDoublePoint pos )
1081{
1082 histoPickerSelected( QPointF( pos.x(), pos.y() ) );
1083}
1084
1085void QgsRasterHistogramWidget::updateHistoMarkers()
1086{
1087 // hack to not update markers
1088 if ( leHistoMin->signalsBlocked() )
1089 return;
1090 // todo error checking
1091 if ( !mpPlot || !mHistoMarkerMin || !mHistoMarkerMax )
1092 return;
1093
1094 const int bandNo = cboHistoBand->currentIndex() + 1;
1095 const QList<int> mySelectedBands = histoSelectedBands();
1096
1097 if ( ( !mHistoShowMarkers && !btnHistoMin->isChecked() && !btnHistoMax->isChecked() ) || ( !mySelectedBands.isEmpty() && !mySelectedBands.contains( bandNo ) ) )
1098 {
1099 mHistoMarkerMin->hide();
1100 mHistoMarkerMax->hide();
1101 mpPlot->replot();
1102 return;
1103 }
1104
1105 double minVal = mHistoMin;
1106 double maxVal = mHistoMax;
1107 const QString minStr = leHistoMin->text();
1108 const QString maxStr = leHistoMax->text();
1109 if ( !minStr.isEmpty() )
1110 minVal = minStr.toDouble();
1111 if ( !maxStr.isEmpty() )
1112 maxVal = maxStr.toDouble();
1113
1114 QPen linePen = QPen( mHistoColors.at( bandNo ) );
1115 linePen.setStyle( Qt::DashLine );
1116 mHistoMarkerMin->setLineStyle( QwtPlotMarker::VLine );
1117 mHistoMarkerMin->setLinePen( linePen );
1118 mHistoMarkerMin->setXValue( minVal );
1119 mHistoMarkerMin->show();
1120 mHistoMarkerMax->setLineStyle( QwtPlotMarker::VLine );
1121 mHistoMarkerMax->setLinePen( linePen );
1122 mHistoMarkerMax->setXValue( maxVal );
1123 mHistoMarkerMax->show();
1124
1125 mpPlot->replot();
1126}
1127
1128
1129QList<int> QgsRasterHistogramWidget::histoSelectedBands()
1130{
1131 QList<int> mySelectedBands;
1132
1133 if ( mHistoShowBands != ShowAll )
1134 {
1135 if ( mHistoShowBands == ShowSelected )
1136 {
1137 mySelectedBands << cboHistoBand->currentIndex() + 1;
1138 }
1139 else if ( mHistoShowBands == ShowRGB )
1140 {
1141 mySelectedBands = rendererSelectedBands();
1142 }
1143 }
1144
1145 return mySelectedBands;
1146}
1147
1148QList<int> QgsRasterHistogramWidget::rendererSelectedBands()
1149{
1150 QList<int> mySelectedBands;
1151
1152 if ( !mRendererWidget )
1153 {
1154 mySelectedBands << -1 << -1 << -1; // make sure we return 3 elements
1155 return mySelectedBands;
1156 }
1157
1158 if ( mRendererName == "singlebandgray"_L1 || mRendererName == "singlebandpseudocolor"_L1 )
1159 {
1160 mySelectedBands << mRendererWidget->selectedBand();
1161 }
1162 else if ( mRendererName == "multibandcolor"_L1 )
1163 {
1164 for ( int i = 0; i <= 2; i++ )
1165 {
1166 mySelectedBands << mRendererWidget->selectedBand( i );
1167 }
1168 }
1169
1170 return mySelectedBands;
1171}
1172
1173QPair<QString, QString> QgsRasterHistogramWidget::rendererMinMax( int bandNo )
1174{
1175 QPair<QString, QString> myMinMax;
1176
1177 if ( !mRendererWidget )
1178 return myMinMax;
1179
1180 if ( mRendererName == "singlebandgray"_L1 || mRendererName == "singlebandpseudocolor"_L1 )
1181 {
1182 if ( bandNo == mRendererWidget->selectedBand() )
1183 {
1184 myMinMax.first = mRendererWidget->min();
1185 myMinMax.second = mRendererWidget->max();
1186 }
1187 }
1188 else if ( mRendererName == "multibandcolor"_L1 )
1189 {
1190 for ( int i = 0; i <= 2; i++ )
1191 {
1192 if ( bandNo == mRendererWidget->selectedBand( i ) )
1193 {
1194 myMinMax.first = mRendererWidget->min( i );
1195 myMinMax.second = mRendererWidget->max( i );
1196 break;
1197 }
1198 }
1199 }
1200
1201 // TODO - there are 2 definitions of raster data type that should be unified
1202 // QgsRasterDataProvider::DataType and Qgis::DataType
1203 // TODO - fix gdal provider: changes data type when nodata value is not found
1204 // this prevents us from getting proper min and max values here
1205 // minStr = QString::number( QgsContrastEnhancement::minimumValuePossible( ( Qgis::DataType )
1206 // mRasterLayer->dataProvider()->dataType( bandNo ) ) );
1207 // maxStr = QString::number( QgsContrastEnhancement::maximumValuePossible( ( Qgis::DataType )
1208 // mRasterLayer->dataProvider()->dataType( bandNo ) ) );
1209
1210 // if we get an empty result, fill with default value (histo min/max)
1211 if ( myMinMax.first.isEmpty() )
1212 myMinMax.first = QLocale().toString( mHistoMin );
1213 if ( myMinMax.second.isEmpty() )
1214 myMinMax.second = QLocale().toString( mHistoMax );
1215
1216 QgsDebugMsgLevel( u"bandNo %1 got min/max [%2] [%3]"_s.arg( bandNo ).arg( myMinMax.first, myMinMax.second ), 2 );
1217
1218 return myMinMax;
1219}
1220
@ Size
Original data source size (and thus resolution) is known, it is not always available,...
Definition qgis.h:4927
DataType
Raster data types.
Definition qgis.h:379
@ Int16
Sixteen bit signed integer (qint16).
Definition qgis.h:384
@ Int8
Eight bit signed integer (qint8) (added in QGIS 3.30).
Definition qgis.h:382
@ UInt16
Sixteen bit unsigned integer (quint16).
Definition qgis.h:383
@ Byte
Eight bit unsigned integer (quint8).
Definition qgis.h:381
@ Int32
Thirty two bit signed integer (qint32).
Definition qgis.h:386
@ UInt32
Thirty two bit unsigned integer (quint32).
Definition qgis.h:385
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)