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