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