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