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