QGIS API Documentation  3.22.4-Białowieża (ce8e65e95e)
qgsrendererrasterpropertieswidget.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsrendererrasterpropertieswidget.cpp
3  ---------------------
4  begin : May 2016
5  copyright : (C) 2016 by Nathan Woodrow
6  email : woodrow dot nathan at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
16 
17 #include "qgis.h"
18 #include "qgsmapcanvas.h"
20 #include "qgshuesaturationfilter.h"
22 #include "qgsrasterlayer.h"
32 #include "qgsapplication.h"
33 
34 
35 #include "qgsmessagelog.h"
36 
37 static void _initRendererWidgetFunctions()
38 {
39  static bool sInitialized = false;
40  if ( sInitialized )
41  return;
42 
49 
50  sInitialized = true;
51 }
52 
53 
54 
56  : QgsMapLayerConfigWidget( layer, canvas, parent )
57  , mRasterLayer( qobject_cast<QgsRasterLayer *>( layer ) )
58 {
59  if ( !mRasterLayer )
60  return;
61 
62  setupUi( this );
63  connect( mResetColorRenderingBtn, &QToolButton::clicked, this, &QgsRendererRasterPropertiesWidget::mResetColorRenderingBtn_clicked );
64 
65  _initRendererWidgetFunctions();
66 
67  mResamplingUtils.initWidgets( mRasterLayer, mZoomedInResamplingComboBox, mZoomedOutResamplingComboBox, mMaximumOversamplingSpinBox, mCbEarlyResampling );
68 
69  connect( cboRenderers, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsRendererRasterPropertiesWidget::rendererChanged );
70 
71  connect( mSliderBrightness, &QAbstractSlider::valueChanged, mBrightnessSpinBox, &QSpinBox::setValue );
72  connect( mBrightnessSpinBox, static_cast < void ( QSpinBox::* )( int ) > ( &QSpinBox::valueChanged ), mSliderBrightness, &QAbstractSlider::setValue );
73  mBrightnessSpinBox->setClearValue( 0 );
74 
75  connect( mSliderContrast, &QAbstractSlider::valueChanged, mContrastSpinBox, &QSpinBox::setValue );
76  connect( mContrastSpinBox, static_cast < void ( QSpinBox::* )( int ) > ( &QSpinBox::valueChanged ), mSliderContrast, &QAbstractSlider::setValue );
77  mContrastSpinBox->setClearValue( 0 );
78 
79  connect( mSliderGamma, &QAbstractSlider::valueChanged, this, &QgsRendererRasterPropertiesWidget::updateGammaSpinBox );
80  connect( mGammaSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsRendererRasterPropertiesWidget::updateGammaSlider );
81  mGammaSpinBox->setClearValue( 1.0 );
82 
83  // Connect saturation slider and spin box
84  connect( sliderSaturation, &QAbstractSlider::valueChanged, spinBoxSaturation, &QSpinBox::setValue );
85  connect( spinBoxSaturation, static_cast < void ( QSpinBox::* )( int ) > ( &QSpinBox::valueChanged ), sliderSaturation, &QAbstractSlider::setValue );
86  spinBoxSaturation->setClearValue( 0 );
87 
88  // Connect colorize strength slider and spin box
89  connect( sliderColorizeStrength, &QAbstractSlider::valueChanged, spinColorizeStrength, &QSpinBox::setValue );
90  connect( spinColorizeStrength, static_cast < void ( QSpinBox::* )( int ) > ( &QSpinBox::valueChanged ), sliderColorizeStrength, &QAbstractSlider::setValue );
91  spinColorizeStrength->setClearValue( 100 );
92 
93  // enable or disable saturation slider and spin box depending on grayscale combo choice
94  connect( comboGrayscale, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsRendererRasterPropertiesWidget::toggleSaturationControls );
95 
96  // enable or disable colorize colorbutton with colorize checkbox
97  connect( mColorizeCheck, &QAbstractButton::toggled, this, &QgsRendererRasterPropertiesWidget::toggleColorizeControls );
98 
99  // Just connect the spin boxes because the sliders update the spinners
100  connect( mBrightnessSpinBox, static_cast < void ( QSpinBox::* )( int ) > ( &QSpinBox::valueChanged ), this, &QgsPanelWidget::widgetChanged );
101  connect( mContrastSpinBox, static_cast < void ( QSpinBox::* )( int ) > ( &QSpinBox::valueChanged ), this, &QgsPanelWidget::widgetChanged );
102  connect( mGammaSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsPanelWidget::widgetChanged );
103  connect( spinBoxSaturation, static_cast < void ( QSpinBox::* )( int ) > ( &QSpinBox::valueChanged ), this, &QgsPanelWidget::widgetChanged );
104  connect( spinColorizeStrength, static_cast < void ( QSpinBox::* )( int ) > ( &QSpinBox::valueChanged ), this, &QgsPanelWidget::widgetChanged );
105  connect( btnColorizeColor, &QgsColorButton::colorChanged, this, &QgsPanelWidget::widgetChanged );
106  connect( mInvertColorsCheck, &QAbstractButton::toggled, this, &QgsPanelWidget::widgetChanged );
107 
108  connect( mBlendModeComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsPanelWidget::widgetChanged );
109  connect( mZoomedInResamplingComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsPanelWidget::widgetChanged );
110  connect( mZoomedOutResamplingComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsPanelWidget::widgetChanged );
111  connect( mMaximumOversamplingSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsPanelWidget::widgetChanged );
112  connect( mCbEarlyResampling, &QAbstractButton::toggled, this, &QgsPanelWidget::widgetChanged );
113 
114  // finally sync to the layer - even though some actions may emit widgetChanged signal,
115  // this is not a problem - nobody is listening to our signals yet
116  syncToLayer( mRasterLayer );
117 
118  connect( mRasterLayer, &QgsMapLayer::styleChanged, this, &QgsRendererRasterPropertiesWidget::refreshAfterStyleChanged );
119 }
120 
122 {
123  mMapCanvas = canvas;
124 }
125 
127 {
128  const QString rendererName = cboRenderers->currentData().toString();
129  setRendererWidget( rendererName );
130  emit widgetChanged();
131 }
132 
134 {
135  if ( QgsBrightnessContrastFilter *brightnessFilter = mRasterLayer->brightnessFilter() )
136  {
137  brightnessFilter->setBrightness( mSliderBrightness->value() );
138  brightnessFilter->setContrast( mSliderContrast->value() );
139  brightnessFilter->setGamma( mGammaSpinBox->value() );
140  }
141 
142  if ( QgsRasterRendererWidget *rendererWidget = dynamic_cast<QgsRasterRendererWidget *>( stackedWidget->currentWidget() ) )
143  {
144  rendererWidget->doComputations();
145 
146  if ( QgsRasterRenderer *newRenderer = rendererWidget->renderer() )
147  {
148  // there are transparency related data stored in renderer instances, but they
149  // are not configured in the widget, so we need to copy them over from existing renderer
150  if ( QgsRasterRenderer *oldRenderer = mRasterLayer->renderer() )
151  newRenderer->copyCommonProperties( oldRenderer, false );
152  mRasterLayer->setRenderer( newRenderer );
153  }
154  }
155 
156  // Hue and saturation controls
157  if ( QgsHueSaturationFilter *hueSaturationFilter = mRasterLayer->hueSaturationFilter() )
158  {
159  hueSaturationFilter->setSaturation( sliderSaturation->value() );
160  hueSaturationFilter->setGrayscaleMode( ( QgsHueSaturationFilter::GrayscaleMode ) comboGrayscale->currentIndex() );
161  hueSaturationFilter->setColorizeOn( mColorizeCheck->checkState() );
162  hueSaturationFilter->setColorizeColor( btnColorizeColor->color() );
163  hueSaturationFilter->setColorizeStrength( sliderColorizeStrength->value() );
164  hueSaturationFilter->setInvertColors( mInvertColorsCheck->isChecked() );
165  }
166 
167  mResamplingUtils.refreshLayerFromWidgets();
168 
169  mRasterLayer->setBlendMode( mBlendModeComboBox->blendMode() );
170 }
171 
173 {
174  mRasterLayer = layer;
175 
176  cboRenderers->blockSignals( true );
177  cboRenderers->clear();
179  const auto constRenderersList = QgsApplication::rasterRendererRegistry()->renderersList();
180  for ( const QString &name : constRenderersList )
181  {
182  if ( QgsApplication::rasterRendererRegistry()->rendererData( name, entry ) )
183  {
184  if ( ( mRasterLayer->rasterType() != QgsRasterLayer::ColorLayer && entry.name != QLatin1String( "singlebandcolordata" ) ) ||
185  ( mRasterLayer->rasterType() == QgsRasterLayer::ColorLayer && entry.name == QLatin1String( "singlebandcolordata" ) ) )
186  {
187  cboRenderers->addItem( entry.icon(), entry.visibleName, entry.name );
188  }
189  }
190  }
191  cboRenderers->setCurrentIndex( -1 );
192  cboRenderers->blockSignals( false );
193 
194  if ( QgsRasterRenderer *renderer = mRasterLayer->renderer() )
195  {
196  setRendererWidget( renderer->type() );
197  }
198 
199  if ( QgsBrightnessContrastFilter *brightnessFilter = mRasterLayer->brightnessFilter() )
200  {
201  mSliderBrightness->setValue( brightnessFilter->brightness() );
202  mSliderContrast->setValue( brightnessFilter->contrast() );
203  mGammaSpinBox->setValue( brightnessFilter->gamma() );
204  }
205 
206  btnColorizeColor->setColorDialogTitle( tr( "Select Color" ) );
207  btnColorizeColor->setContext( QStringLiteral( "symbology" ) );
208 
209  // Hue and saturation color control
210  //set hue and saturation controls to current values
211  if ( const QgsHueSaturationFilter *hueSaturationFilter = mRasterLayer->hueSaturationFilter() )
212  {
213  sliderSaturation->setValue( hueSaturationFilter->saturation() );
214  comboGrayscale->setCurrentIndex( ( int ) hueSaturationFilter->grayscaleMode() );
215 
216  // Set initial state of saturation controls based on grayscale mode choice
217  toggleSaturationControls( static_cast<int>( hueSaturationFilter->grayscaleMode() ) );
218 
219  // Set initial state of colorize controls
220  mColorizeCheck->setChecked( hueSaturationFilter->colorizeOn() );
221  btnColorizeColor->setColor( hueSaturationFilter->colorizeColor() );
222  toggleColorizeControls( hueSaturationFilter->colorizeOn() );
223  sliderColorizeStrength->setValue( hueSaturationFilter->colorizeStrength() );
224 
225  mInvertColorsCheck->setChecked( hueSaturationFilter->invertColors() );
226  }
227 
228  //blend mode
229  mBlendModeComboBox->setBlendMode( mRasterLayer->blendMode() );
230 
231  //set combo boxes to current resampling types
232  mResamplingUtils.refreshWidgetsFromLayer();
233 }
234 
235 void QgsRendererRasterPropertiesWidget::mResetColorRenderingBtn_clicked()
236 {
237  mBlendModeComboBox->setBlendMode( QPainter::CompositionMode_SourceOver );
238  mSliderBrightness->setValue( 0 );
239  mSliderContrast->setValue( 0 );
240  mGammaSpinBox->setValue( 1.0 );
241  sliderSaturation->setValue( 0 );
242  comboGrayscale->setCurrentIndex( ( int ) QgsHueSaturationFilter::GrayscaleOff );
243  mColorizeCheck->setChecked( false );
244  sliderColorizeStrength->setValue( 100 );
245  mInvertColorsCheck->setChecked( false );
246 }
247 
248 void QgsRendererRasterPropertiesWidget::toggleSaturationControls( int grayscaleMode )
249 {
250  // Enable or disable saturation controls based on choice of grayscale mode
251  if ( grayscaleMode == 0 )
252  {
253  sliderSaturation->setEnabled( true );
254  spinBoxSaturation->setEnabled( true );
255  }
256  else
257  {
258  sliderSaturation->setEnabled( false );
259  spinBoxSaturation->setEnabled( false );
260  }
261  emit widgetChanged();
262 }
263 
264 void QgsRendererRasterPropertiesWidget::toggleColorizeControls( bool colorizeEnabled )
265 {
266  // Enable or disable colorize controls based on checkbox
267  btnColorizeColor->setEnabled( colorizeEnabled );
268  sliderColorizeStrength->setEnabled( colorizeEnabled );
269  spinColorizeStrength->setEnabled( colorizeEnabled );
270  emit widgetChanged();
271 }
272 
273 void QgsRendererRasterPropertiesWidget::setRendererWidget( const QString &rendererName )
274 {
275  QgsDebugMsgLevel( "rendererName = " + rendererName, 3 );
276  QgsRasterRendererWidget *oldWidget = mRendererWidget;
277 
278  int alphaBand = -1;
279  double opacity = 1;
280  QColor nodataColor;
281  if ( QgsRasterRenderer *oldRenderer = mRasterLayer->renderer() )
282  {
283  // Retain alpha band and opacity when switching renderer
284  alphaBand = oldRenderer->alphaBand();
285  opacity = oldRenderer->opacity();
286  nodataColor = oldRenderer->nodataColor();
287  }
288 
289  QgsRasterRendererRegistryEntry rendererEntry;
290  if ( QgsApplication::rasterRendererRegistry()->rendererData( rendererName, rendererEntry ) )
291  {
292  if ( rendererEntry.widgetCreateFunction ) // Single band color data renderer e.g. has no widget
293  {
294  QgsDebugMsgLevel( QStringLiteral( "renderer has widgetCreateFunction" ), 3 );
295  // Current canvas extent (used to calc min/max) in layer CRS
296  const QgsRectangle myExtent = mMapCanvas->mapSettings().outputExtentToLayerExtent( mRasterLayer, mMapCanvas->extent() );
297  if ( oldWidget )
298  {
299  std::unique_ptr< QgsRasterRenderer > oldRenderer( oldWidget->renderer() );
300  if ( !oldRenderer || oldRenderer->type() != rendererName )
301  {
302  if ( rendererName == QLatin1String( "singlebandgray" ) )
303  {
304  whileBlocking( mRasterLayer )->setRenderer( QgsApplication::rasterRendererRegistry()->defaultRendererForDrawingStyle( QgsRaster::SingleBandGray, mRasterLayer->dataProvider() ) );
305  whileBlocking( mRasterLayer )->setDefaultContrastEnhancement();
306  }
307  else if ( rendererName == QLatin1String( "multibandcolor" ) )
308  {
309  whileBlocking( mRasterLayer )->setRenderer( QgsApplication::rasterRendererRegistry()->defaultRendererForDrawingStyle( QgsRaster::MultiBandColor, mRasterLayer->dataProvider() ) );
310  whileBlocking( mRasterLayer )->setDefaultContrastEnhancement();
311  }
312  }
313  }
314  mRasterLayer->renderer()->setAlphaBand( alphaBand );
315  mRasterLayer->renderer()->setOpacity( opacity );
316  mRasterLayer->renderer()->setNodataColor( nodataColor );
317  mRendererWidget = rendererEntry.widgetCreateFunction( mRasterLayer, myExtent );
318  mRendererWidget->setMapCanvas( mMapCanvas );
319  connect( mRendererWidget, &QgsRasterRendererWidget::widgetChanged, this, &QgsPanelWidget::widgetChanged );
320  stackedWidget->addWidget( mRendererWidget );
321  stackedWidget->setCurrentWidget( mRendererWidget );
322  if ( oldWidget )
323  {
324  // Compare used bands in new and old renderer and reset transparency dialog if different
325  QgsRasterRenderer *oldRenderer = oldWidget->renderer();
326  QgsRasterRenderer *newRenderer = mRendererWidget->renderer();
327 #if 0
328  QList<int> oldBands = oldRenderer->usesBands();
329  QList<int> newBands = newRenderer->usesBands();
330 
331  if ( oldBands != newBands )
332  {
333  populateTransparencyTable( newRenderer );
334  }
335 #endif
336 
337  delete oldRenderer;
338  delete newRenderer;
339  }
340  }
341  }
342 
343  if ( mRendererWidget != oldWidget )
344  delete oldWidget;
345 
346  const int widgetIndex = cboRenderers->findData( rendererName );
347  if ( widgetIndex != -1 )
348  {
349  whileBlocking( cboRenderers )->setCurrentIndex( widgetIndex );
350  }
351 
352 }
353 
354 void QgsRendererRasterPropertiesWidget::refreshAfterStyleChanged()
355 {
356  if ( mRendererWidget )
357  {
358  QgsRasterRenderer *renderer = mRasterLayer->renderer();
359  if ( QgsMultiBandColorRenderer *mbcr = dynamic_cast<QgsMultiBandColorRenderer *>( renderer ) )
360  {
361  const QgsContrastEnhancement *redCe = mbcr->redContrastEnhancement();
362  if ( redCe )
363  {
364  mRendererWidget->setMin( QLocale().toString( redCe->minimumValue() ), 0 );
365  mRendererWidget->setMax( QLocale().toString( redCe->maximumValue() ), 0 );
366  }
367  const QgsContrastEnhancement *greenCe = mbcr->greenContrastEnhancement();
368  if ( greenCe )
369  {
370  mRendererWidget->setMin( QLocale().toString( greenCe->minimumValue() ), 1 );
371  mRendererWidget->setMax( QLocale().toString( greenCe->maximumValue() ), 1 );
372  }
373  const QgsContrastEnhancement *blueCe = mbcr->blueContrastEnhancement();
374  if ( blueCe )
375  {
376  mRendererWidget->setMin( QLocale().toString( blueCe->minimumValue() ), 2 );
377  mRendererWidget->setMax( QLocale().toString( blueCe->maximumValue() ), 2 );
378  }
379  }
380  else if ( QgsSingleBandGrayRenderer *sbgr = dynamic_cast<QgsSingleBandGrayRenderer *>( renderer ) )
381  {
382  const QgsContrastEnhancement *ce = sbgr->contrastEnhancement();
383  if ( ce )
384  {
385  mRendererWidget->setMin( QLocale().toString( ce->minimumValue() ) );
386  mRendererWidget->setMax( QLocale().toString( ce->maximumValue() ) );
387  }
388  }
389  }
390 }
391 
392 void QgsRendererRasterPropertiesWidget::updateGammaSpinBox( int value )
393 {
394  mGammaSpinBox->setValue( value / 100.0 );
395 }
396 
397 void QgsRendererRasterPropertiesWidget::updateGammaSlider( double value )
398 {
399  mSliderGamma->setValue( value * 100 );
400 }
static QgsRasterRendererRegistry * rasterRendererRegistry()
Returns the application's raster renderer registry, used for managing raster layer renderers.
Brightness/contrast and gamma correction filter pipe for rasters.
void colorChanged(const QColor &color)
Emitted whenever a new color is set for the button.
Manipulates raster or point cloud pixel values so that they enhanceContrast or clip into a specified ...
double minimumValue() const
Returns the minimum value for the contrast enhancement range.
double maximumValue() const
Returns the maximum value for the contrast enhancement range.
static QgsRasterRendererWidget * create(QgsRasterLayer *layer, const QgsRectangle &extent)
Factory method to create the renderer for this type.
Color and saturation filter pipe for rasters.
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:89
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
QgsRectangle extent() const
Returns the current zoom extent of the map canvas.
A panel widget that can be shown in the map style dock.
Base class for all map layer types.
Definition: qgsmaplayer.h:73
void setBlendMode(QPainter::CompositionMode blendMode)
Set the blending mode used for rendering a layer.
QPainter::CompositionMode blendMode() const
Returns the current blending mode for a layer.
void styleChanged()
Signal emitted whenever a change affects the layer's style.
QgsRectangle outputExtentToLayerExtent(const QgsMapLayer *layer, QgsRectangle extent) const
transform bounding box from output CRS to layer's CRS
static QgsRasterRendererWidget * create(QgsRasterLayer *layer, const QgsRectangle &extent)
Renderer for multiband images with the color components.
static QgsRasterRendererWidget * create(QgsRasterLayer *layer, const QgsRectangle &extent)
void widgetChanged()
Emitted when the widget state changes.
static QgsRasterRendererWidget * create(QgsRasterLayer *layer, const QgsRectangle &extent)
Widget creation function (mainly for the use by the renderer registry)
Represents a raster layer.
QgsBrightnessContrastFilter * brightnessFilter() const
Returns the raster's brightness/contrast filter.
LayerType rasterType()
Returns the raster layer type (which is a read only property).
QgsRasterRenderer * renderer() const
Returns the raster's renderer.
QgsRasterDataProvider * dataProvider() override
Returns the source data provider.
void setRenderer(QgsRasterRenderer *renderer)
Sets the raster's renderer.
QgsHueSaturationFilter * hueSaturationFilter() const
Returns the raster's hue/saturation filter.
void insertWidgetFunction(const QString &rendererName, QgsRasterRendererWidgetCreateFunc func)
Abstract base class for widgets which configure a QgsRasterRenderer.
virtual void setMapCanvas(QgsMapCanvas *canvas)
Sets the map canvas associated with the widget.
virtual void setMax(const QString &value, int index=0)
virtual void setMin(const QString &value, int index=0)
virtual QgsRasterRenderer * renderer()=0
Creates a new renderer, using the properties defined in the widget.
void widgetChanged()
Emitted when something on the widget has changed.
Raster renderer pipe that applies colors to a raster.
void setAlphaBand(int band)
void setOpacity(double opacity)
Sets the opacity for the renderer, where opacity is a value between 0 (totally transparent) and 1....
virtual QList< int > usesBands() const
Returns a list of band numbers used by the renderer.
void setNodataColor(const QColor &color)
Sets the color to use for shading nodata pixels.
@ MultiBandColor
Definition: qgsraster.h:100
@ SingleBandGray
Definition: qgsraster.h:92
A rectangle specified with double values.
Definition: qgsrectangle.h:42
void rendererChanged()
called when user changes renderer type
QgsRendererRasterPropertiesWidget(QgsMapLayer *layer, QgsMapCanvas *canvas, QWidget *parent=nullptr)
A widget to hold the renderer properties for a raster layer.
void syncToLayer(QgsRasterLayer *layer)
Sync the widget to the given layer.
void setMapCanvas(QgsMapCanvas *canvas)
Sets the map canvas associated with the dialog.
void apply() override
Apply the changes from the dialog to the layer.
static QgsRasterRendererWidget * create(QgsRasterLayer *layer, const QgsRectangle &extent)
Raster renderer pipe for single band gray.
static QgsRasterRendererWidget * create(QgsRasterLayer *layer, const QgsRectangle &extent)
Creates new raster renderer widget.
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition: qgis.h:1185
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
Registry for raster renderer entries.
QgsRasterRendererWidgetCreateFunc widgetCreateFunction