1 /***************************************************************************
2   qgsrasterlayerproperties.cpp  -  description
3   -------------------
4       begin                : 1/1/2004
5       copyright            : (C) 2004 Tim Sutton
6       email                : tim@linfiniti.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 <limits>
19 #include <typeinfo>
20 
21 #include "qgsgui.h"
22 #include "qgsapplication.h"
23 #include "qgsbrightnesscontrastfilter.h"
24 #include "qgscontrastenhancement.h"
25 #include "qgscoordinatetransform.h"
26 #include "qgsprojectionselectiondialog.h"
27 #include "qgslogger.h"
28 #include "qgsmapcanvas.h"
29 #include "qgsmaplayerstyleguiutils.h"
30 #include "qgsmaptoolemitpoint.h"
31 #include "qgsmaptopixel.h"
32 #include "qgsmetadatawidget.h"
33 #include "qgsmultibandcolorrenderer.h"
34 #include "qgsmultibandcolorrendererwidget.h"
35 #include "qgsnative.h"
36 #include "qgspalettedrendererwidget.h"
37 #include "qgsproject.h"
38 #include "qgsrasterbandstats.h"
39 #include "qgsrastercontourrendererwidget.h"
40 #include "qgsrasterdataprovider.h"
41 #include "qgsrasterhistogramwidget.h"
42 #include "qgsrasteridentifyresult.h"
43 #include "qgsrasterlayer.h"
44 #include "qgsrasterlayerproperties.h"
45 #include "qgsrasterpyramid.h"
46 #include "qgsrasterrange.h"
47 #include "qgsrasterrenderer.h"
48 #include "qgsrasterrendererregistry.h"
49 #include "qgsrastertransparency.h"
50 #include "qgssinglebandgrayrendererwidget.h"
51 #include "qgssinglebandpseudocolorrendererwidget.h"
52 #include "qgshuesaturationfilter.h"
53 #include "qgshillshaderendererwidget.h"
54 #include "qgssettings.h"
55 #include "qgsdatumtransformdialog.h"
56 #include "qgsmaplayerlegend.h"
57 #include "qgsfileutils.h"
58 #include "qgswebview.h"
59 #include "qgsvectorlayer.h"
60 #include "qgsprovidermetadata.h"
61 #include "qgsproviderregistry.h"
62 #include "qgsrasterlayertemporalproperties.h"
63 #include "qgsdoublevalidator.h"
64 
65 #include "qgsrasterlayertemporalpropertieswidget.h"
66 #include "qgsprojecttimesettings.h"
67 
68 #include <QDesktopServices>
69 #include <QTableWidgetItem>
70 #include <QHeaderView>
71 #include <QTextStream>
72 #include <QFile>
73 #include <QFileDialog>
74 #include <QMessageBox>
75 #include <QPainter>
76 #include <QLinearGradient>
77 #include <QPainterPath>
78 #include <QPolygonF>
79 #include <QColorDialog>
80 #include <QList>
81 #include <QMouseEvent>
82 #include <QVector>
83 #include <QUrl>
84 #include <QMenu>
85 #include <QScreen>
86 
QgsRasterLayerProperties(QgsMapLayer * lyr,QgsMapCanvas * canvas,QWidget * parent,Qt::WindowFlags fl)87 QgsRasterLayerProperties::QgsRasterLayerProperties( QgsMapLayer *lyr, QgsMapCanvas *canvas, QWidget *parent, Qt::WindowFlags fl )
88   : QgsOptionsDialogBase( QStringLiteral( "RasterLayerProperties" ), parent, fl )
89     // Constant that signals property not used.
90   , TRSTRING_NOT_SET( tr( "Not Set" ) )
91   , mDefaultStandardDeviation( 0 )
92   , mDefaultRedBand( 0 )
93   , mDefaultGreenBand( 0 )
94   , mDefaultBlueBand( 0 )
95   , mRasterLayer( qobject_cast<QgsRasterLayer *>( lyr ) )
96   , mGradientHeight( 0.0 )
97   , mGradientWidth( 0.0 )
98   , mMapCanvas( canvas )
99   , mMetadataFilled( false )
100 {
101   mGrayMinimumMaximumEstimated = true;
102   mRGBMinimumMaximumEstimated = true;
103 
104   setupUi( this );
105   connect( mLayerOrigNameLineEd, &QLineEdit::textEdited, this, &QgsRasterLayerProperties::mLayerOrigNameLineEd_textEdited );
106   connect( buttonBuildPyramids, &QPushButton::clicked, this, &QgsRasterLayerProperties::buttonBuildPyramids_clicked );
107   connect( pbnAddValuesFromDisplay, &QToolButton::clicked, this, &QgsRasterLayerProperties::pbnAddValuesFromDisplay_clicked );
108   connect( pbnAddValuesManually, &QToolButton::clicked, this, &QgsRasterLayerProperties::pbnAddValuesManually_clicked );
109   connect( mCrsSelector, &QgsProjectionSelectionWidget::crsChanged, this, &QgsRasterLayerProperties::mCrsSelector_crsChanged );
110   connect( pbnDefaultValues, &QToolButton::clicked, this, &QgsRasterLayerProperties::pbnDefaultValues_clicked );
111   connect( pbnExportTransparentPixelValues, &QToolButton::clicked, this, &QgsRasterLayerProperties::pbnExportTransparentPixelValues_clicked );
112   connect( pbnImportTransparentPixelValues, &QToolButton::clicked, this, &QgsRasterLayerProperties::pbnImportTransparentPixelValues_clicked );
113   connect( pbnRemoveSelectedRow, &QToolButton::clicked, this, &QgsRasterLayerProperties::pbnRemoveSelectedRow_clicked );
114   connect( mRenderTypeComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsRasterLayerProperties::mRenderTypeComboBox_currentIndexChanged );
115   connect( mResetColorRenderingBtn, &QToolButton::clicked, this, &QgsRasterLayerProperties::mResetColorRenderingBtn_clicked );
116   // QgsOptionsDialogBase handles saving/restoring of geometry, splitter and current tab states,
117   // switching vertical tabs between icon/text to icon-only modes (splitter collapsed to left),
118   // and connecting QDialogButtonBox's accepted/rejected signals to dialog's accept/reject slots
119   initOptionsBase( false );
120   connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsRasterLayerProperties::showHelp );
121 
122   connect( mSetEndAsStartStaticButton, &QPushButton::clicked, this, &QgsRasterLayerProperties::setEndAsStartStaticButton_clicked );
123   connect( mProjectTemporalRange, &QRadioButton::toggled, this, &QgsRasterLayerProperties::passProjectTemporalRange_toggled );
124   connect( mStaticTemporalRange, &QRadioButton::toggled, this, &QgsRasterLayerProperties::staticTemporalRange_toggled );
125 
126   connect( mStaticTemporalRange, &QRadioButton::toggled, mStaticWmstFrame, &QWidget::setEnabled );
127   connect( mReferenceTime, &QCheckBox::toggled, mWmstReferenceTimeFrame, &QWidget::setEnabled );
128 
129   if ( mRasterLayer && mRasterLayer->temporalProperties() )
130     connect( mRasterLayer->temporalProperties(), &QgsRasterLayerTemporalProperties::changed, this, &QgsRasterLayerProperties::temporalPropertiesChange );
131 
132   mBtnStyle = new QPushButton( tr( "Style" ) );
133   QMenu *menuStyle = new QMenu( this );
134   menuStyle->addAction( tr( "Load Style…" ), this, &QgsRasterLayerProperties::loadStyle_clicked );
135   menuStyle->addAction( tr( "Save Style…" ), this, &QgsRasterLayerProperties::saveStyleAs_clicked );
136   menuStyle->addSeparator();
137   menuStyle->addAction( tr( "Save as Default" ), this, &QgsRasterLayerProperties::saveDefaultStyle_clicked );
138   menuStyle->addAction( tr( "Restore Default" ), this, &QgsRasterLayerProperties::loadDefaultStyle_clicked );
139   mBtnStyle->setMenu( menuStyle );
140   connect( menuStyle, &QMenu::aboutToShow, this, &QgsRasterLayerProperties::aboutToShowStyleMenu );
141   buttonBox->addButton( mBtnStyle, QDialogButtonBox::ResetRole );
142 
143   mBtnMetadata = new QPushButton( tr( "Metadata" ), this );
144   QMenu *menuMetadata = new QMenu( this );
145   mActionLoadMetadata = menuMetadata->addAction( tr( "Load Metadata…" ), this, &QgsRasterLayerProperties::loadMetadata );
146   mActionSaveMetadataAs = menuMetadata->addAction( tr( "Save Metadata…" ), this, &QgsRasterLayerProperties::saveMetadataAs );
147   menuMetadata->addSeparator();
148   menuMetadata->addAction( tr( "Save as Default" ), this, &QgsRasterLayerProperties::saveDefaultMetadata );
149   menuMetadata->addAction( tr( "Restore Default" ), this, &QgsRasterLayerProperties::loadDefaultMetadata );
150   mBtnMetadata->setMenu( menuMetadata );
151   buttonBox->addButton( mBtnMetadata, QDialogButtonBox::ResetRole );
152 
153   connect( lyr->styleManager(), &QgsMapLayerStyleManager::currentStyleChanged, this, &QgsRasterLayerProperties::syncToLayer );
154 
155   connect( this, &QDialog::accepted, this, &QgsRasterLayerProperties::apply );
156   connect( this, &QDialog::rejected, this, &QgsRasterLayerProperties::onCancel );
157 
158   connect( buttonBox->button( QDialogButtonBox::Apply ), &QAbstractButton::clicked, this, &QgsRasterLayerProperties::apply );
159 
160   // brightness/contrast controls
161   connect( mSliderBrightness, &QAbstractSlider::valueChanged, mBrightnessSpinBox, &QSpinBox::setValue );
162   connect( mBrightnessSpinBox, static_cast < void ( QSpinBox::* )( int ) > ( &QSpinBox::valueChanged ), mSliderBrightness, &QAbstractSlider::setValue );
163   mBrightnessSpinBox->setClearValue( 0 );
164 
165   connect( mSliderContrast, &QAbstractSlider::valueChanged, mContrastSpinBox, &QSpinBox::setValue );
166   connect( mContrastSpinBox, static_cast < void ( QSpinBox::* )( int ) > ( &QSpinBox::valueChanged ), mSliderContrast, &QAbstractSlider::setValue );
167   mContrastSpinBox->setClearValue( 0 );
168 
169   // gamma correction controls
170   connect( mSliderGamma, &QAbstractSlider::valueChanged, this, &QgsRasterLayerProperties::updateGammaSpinBox );
171   connect( mGammaSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsRasterLayerProperties::updateGammaSlider );
172   mGammaSpinBox->setClearValue( 1.0 );
173 
174   // Connect saturation slider and spin box
175   connect( sliderSaturation, &QAbstractSlider::valueChanged, spinBoxSaturation, &QSpinBox::setValue );
176   connect( spinBoxSaturation, static_cast < void ( QSpinBox::* )( int ) > ( &QSpinBox::valueChanged ), sliderSaturation, &QAbstractSlider::setValue );
177   spinBoxSaturation->setClearValue( 0 );
178 
179   // Connect colorize strength slider and spin box
180   connect( sliderColorizeStrength, &QAbstractSlider::valueChanged, spinColorizeStrength, &QSpinBox::setValue );
181   connect( spinColorizeStrength, static_cast < void ( QSpinBox::* )( int ) > ( &QSpinBox::valueChanged ), sliderColorizeStrength, &QAbstractSlider::setValue );
182   spinColorizeStrength->setClearValue( 100 );
183 
184   // enable or disable saturation slider and spin box depending on grayscale combo choice
185   connect( comboGrayscale, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsRasterLayerProperties::toggleSaturationControls );
186 
187   // enable or disable colorize colorbutton with colorize checkbox
188   connect( mColorizeCheck, &QAbstractButton::toggled, this, &QgsRasterLayerProperties::toggleColorizeControls );
189 
190   // enable or disable Build Pyramids button depending on selection in pyramid list
191   connect( lbxPyramidResolutions, &QListWidget::itemSelectionChanged, this, &QgsRasterLayerProperties::toggleBuildPyramidsButton );
192 
193   connect( mRefreshLayerCheckBox, &QCheckBox::toggled, mRefreshLayerIntervalSpinBox, &QDoubleSpinBox::setEnabled );
194 
195   // set up the scale based layer visibility stuff....
196   mScaleRangeWidget->setMapCanvas( mMapCanvas );
197   chkUseScaleDependentRendering->setChecked( lyr->hasScaleBasedVisibility() );
198   mScaleRangeWidget->setScaleRange( lyr->minimumScale(), lyr->maximumScale() );
199 
200   leNoDataValue->setValidator( new QgsDoubleValidator( -std::numeric_limits<double>::max(), std::numeric_limits<double>::max(), 1000, this ) );
201 
202   // build GUI components
203   QIcon myPyramidPixmap( QgsApplication::getThemeIcon( "/mIconPyramid.svg" ) );
204   QIcon myNoPyramidPixmap( QgsApplication::getThemeIcon( "/mIconNoPyramid.svg" ) );
205 
206   pbnAddValuesManually->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/symbologyAdd.svg" ) ) );
207   pbnAddValuesFromDisplay->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionContextHelp.png" ) ) );
208   pbnRemoveSelectedRow->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/symbologyRemove.svg" ) ) );
209   pbnDefaultValues->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionOpenTable.svg" ) ) );
210   pbnImportTransparentPixelValues->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionFileOpen.svg" ) ) );
211   pbnExportTransparentPixelValues->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionFileSave.svg" ) ) );
212 
213   if ( mMapCanvas )
214   {
215     mPixelSelectorTool = qgis::make_unique<QgsMapToolEmitPoint>( canvas );
216     connect( mPixelSelectorTool.get(), &QgsMapToolEmitPoint::canvasClicked, this, &QgsRasterLayerProperties::pixelSelected );
217     connect( mPixelSelectorTool.get(), &QgsMapToolEmitPoint::deactivated, this, &QgsRasterLayerProperties::restoreWindowModality );
218   }
219   else
220   {
221     pbnAddValuesFromDisplay->setEnabled( false );
222   }
223 
224   if ( !mRasterLayer )
225   {
226     return;
227   }
228 
229   QgsRasterDataProvider *provider = mRasterLayer->dataProvider();
230 
231   // Only do pyramids if dealing directly with GDAL.
232   if ( provider && provider->capabilities() & QgsRasterDataProvider::BuildPyramids )
233   {
234     // initialize resampling methods
235     cboResamplingMethod->clear();
236 
237     const auto constProviderType = QgsRasterDataProvider::pyramidResamplingMethods( mRasterLayer->providerType() );
238     for ( QPair<QString, QString> method : constProviderType )
239     {
240       cboResamplingMethod->addItem( method.second, method.first );
241     }
242 
243     // keep it in sync with qgsrasterpyramidsoptionwidget.cpp
244     QString prefix = provider->name() + "/driverOptions/_pyramids/";
245     QgsSettings mySettings;
246     QString defaultMethod = mySettings.value( prefix + "resampling", "AVERAGE" ).toString();
247     int idx = cboResamplingMethod->findData( defaultMethod );
248     if ( idx >= 0 )
249       cboResamplingMethod->setCurrentIndex( idx );
250 
251 
252     // build pyramid list
253     QList< QgsRasterPyramid > myPyramidList = provider->buildPyramidList();
254     QList< QgsRasterPyramid >::iterator myRasterPyramidIterator;
255 
256     for ( myRasterPyramidIterator = myPyramidList.begin();
257           myRasterPyramidIterator != myPyramidList.end();
258           ++myRasterPyramidIterator )
259     {
260       if ( myRasterPyramidIterator->exists )
261       {
262         lbxPyramidResolutions->addItem( new QListWidgetItem( myPyramidPixmap,
263                                         QString::number( myRasterPyramidIterator->xDim ) + QStringLiteral( " x " ) +
264                                         QString::number( myRasterPyramidIterator->yDim ) ) );
265       }
266       else
267       {
268         lbxPyramidResolutions->addItem( new QListWidgetItem( myNoPyramidPixmap,
269                                         QString::number( myRasterPyramidIterator->xDim ) + QStringLiteral( " x " ) +
270                                         QString::number( myRasterPyramidIterator->yDim ) ) );
271       }
272     }
273   }
274   else
275   {
276     // disable Pyramids tab completely
277     mOptsPage_Pyramids->setEnabled( false );
278   }
279 
280   // We can calculate histogram for all data sources but estimated only if
281   // size is unknown - could also be enabled if well supported (estimated histogram
282   // and let user know that it is estimated)
283   if ( !provider || !( provider->capabilities() & QgsRasterDataProvider::Size ) )
284   {
285     // disable Histogram tab completely
286     mOptsPage_Histogram->setEnabled( false );
287   }
288 
289   QVBoxLayout *layout = new QVBoxLayout( metadataFrame );
290   layout->setContentsMargins( 0, 0, 0, 0 );
291   mMetadataWidget = new QgsMetadataWidget( this, mRasterLayer );
292   mMetadataWidget->layout()->setContentsMargins( 0, 0, 0, 0 );
293   mMetadataWidget->setMapCanvas( mMapCanvas );
294   layout->addWidget( mMetadataWidget );
295   metadataFrame->setLayout( layout );
296 
297   QVBoxLayout *temporalLayout = new QVBoxLayout( temporalFrame );
298   temporalLayout->setContentsMargins( 0, 0, 0, 0 );
299   mTemporalWidget = new QgsRasterLayerTemporalPropertiesWidget( this, mRasterLayer );
300   temporalLayout->addWidget( mTemporalWidget );
301 
302   setSourceStaticTimeState();
303   mWmstGroup->setVisible( mRasterLayer->providerType() == QLatin1String( "wms" ) && mRasterLayer->dataProvider() && mRasterLayer->dataProvider()->temporalCapabilities()->hasTemporalCapabilities() );
304 
305   // This group is used to define the temporal capabilities of the PG raster layer
306   if ( mRasterLayer->dataProvider() && mRasterLayer->providerType() == QLatin1String( "postgresraster" ) )
307   {
308     mPostgresRasterTemporalGroup->setEnabled( true );
309     mPostgresRasterTemporalGroup->setVisible( true );
310     mPostgresRasterTemporalGroup->setChecked( false );
311     const QgsFields fields { mRasterLayer->dataProvider()->fields() };
312     mPostgresRasterTemporalFieldComboBox->setFields( fields );
313     mPostgresRasterTemporalFieldComboBox->setFilters( QgsFieldProxyModel::Filter::Date |
314         QgsFieldProxyModel::Filter::DateTime |
315         QgsFieldProxyModel::Filter::String );
316     mPostgresRasterTemporalFieldComboBox->setAllowEmptyFieldName( true );
317     connect( mPostgresRasterTemporalFieldComboBox, &QgsFieldComboBox::fieldChanged, this, [ = ]( const QString & fieldName )
318     {
319       mPostgresRasterDefaultTime->setEnabled( ! fieldName.isEmpty() );
320     } );
321     mPostgresRasterDefaultTime->setAllowNull( true );
322     mPostgresRasterDefaultTime->setEmpty();
323     if ( mRasterLayer->dataProvider()->uri().hasParam( QStringLiteral( "temporalFieldIndex" ) ) )
324     {
325       bool ok;
326       const int fieldIdx {  mRasterLayer->dataProvider()->uri().param( QStringLiteral( "temporalFieldIndex" ) ).toInt( &ok ) };
327       if ( ok && fields.exists( fieldIdx ) )
328       {
329         mPostgresRasterTemporalGroup->setChecked( true );
330         mPostgresRasterTemporalFieldComboBox->setField( fields.field( fieldIdx ).name() );
331         if ( mRasterLayer->dataProvider()->uri().hasParam( QStringLiteral( "temporalDefaultTime" ) ) )
332         {
333           const QDateTime defaultDateTime { QDateTime::fromString( mRasterLayer->dataProvider()->uri().param( QStringLiteral( "temporalDefaultTime" ) ), Qt::DateFormat::ISODate ) };
334           if ( defaultDateTime.isValid() )
335           {
336             mPostgresRasterDefaultTime->setDateTime( defaultDateTime );
337           }
338         }
339       }
340     }
341   }
342   else
343   {
344     mPostgresRasterTemporalGroup->setEnabled( false );
345     mPostgresRasterTemporalGroup->setVisible( false );
346   }
347 
348   QgsDebugMsg( "Setting crs to " + mRasterLayer->crs().toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED ) );
349   QgsDebugMsg( "Setting crs to " + mRasterLayer->crs().userFriendlyIdentifier() );
350   mCrsSelector->setCrs( mRasterLayer->crs() );
351 
352   // Set text for pyramid info box
353   QString pyramidFormat( QStringLiteral( "<h2>%1</h2><p>%2 %3 %4</p><b><font color='red'><p>%5</p><p>%6</p>" ) );
354   QString pyramidHeader    = tr( "Description" );
355   QString pyramidSentence1 = tr( "Large resolution raster layers can slow navigation in QGIS." );
356   QString pyramidSentence2 = tr( "By creating lower resolution copies of the data (pyramids) performance can be considerably improved as QGIS selects the most suitable resolution to use depending on the level of zoom." );
357   QString pyramidSentence3 = tr( "You must have write access in the directory where the original data is stored to build pyramids." );
358   QString pyramidSentence4 = tr( "Please note that building internal pyramids may alter the original data file and once created they cannot be removed!" );
359   QString pyramidSentence5 = tr( "Please note that building internal pyramids could corrupt your image - always make a backup of your data first!" );
360 
361   tePyramidDescription->setHtml( pyramidFormat.arg( pyramidHeader,
362                                  pyramidSentence1,
363                                  pyramidSentence2,
364                                  pyramidSentence3,
365                                  pyramidSentence4,
366                                  pyramidSentence5 ) );
367 
368   //resampling
369   mResamplingGroupBox->setSaveCheckedState( true );
370   mResamplingUtils.initWidgets( mRasterLayer, mZoomedInResamplingComboBox, mZoomedOutResamplingComboBox, mMaximumOversamplingSpinBox, mCbEarlyResampling );
371   mResamplingUtils.refreshWidgetsFromLayer();
372 
373   const QgsRasterRenderer *renderer = mRasterLayer->renderer();
374 
375   btnColorizeColor->setColorDialogTitle( tr( "Select Color" ) );
376   btnColorizeColor->setContext( QStringLiteral( "symbology" ) );
377 
378   // Hue and saturation color control
379   const QgsHueSaturationFilter *hueSaturationFilter = mRasterLayer->hueSaturationFilter();
380   //set hue and saturation controls to current values
381   if ( hueSaturationFilter )
382   {
383     sliderSaturation->setValue( hueSaturationFilter->saturation() );
384     comboGrayscale->setCurrentIndex( ( int ) hueSaturationFilter->grayscaleMode() );
385 
386     // Set initial state of saturation controls based on grayscale mode choice
387     toggleSaturationControls( static_cast<int>( hueSaturationFilter->grayscaleMode() ) );
388 
389     // Set initial state of colorize controls
390     mColorizeCheck->setChecked( hueSaturationFilter->colorizeOn() );
391     btnColorizeColor->setColor( hueSaturationFilter->colorizeColor() );
392     toggleColorizeControls( hueSaturationFilter->colorizeOn() );
393     sliderColorizeStrength->setValue( hueSaturationFilter->colorizeStrength() );
394   }
395 
396   //blend mode
397   mBlendModeComboBox->setBlendMode( mRasterLayer->blendMode() );
398 
399   //transparency band
400   if ( provider )
401   {
402     cboxTransparencyBand->setShowNotSetOption( true, tr( "None" ) );
403     cboxTransparencyBand->setLayer( mRasterLayer );
404 
405 // Alpha band is set in sync()
406 #if 0
407     if ( renderer )
408     {
409       QgsDebugMsg( QStringLiteral( "alphaBand = %1" ).arg( renderer->alphaBand() ) );
410       if ( renderer->alphaBand() > 0 )
411       {
412         cboxTransparencyBand->setCurrentIndex( cboxTransparencyBand->findData( renderer->alphaBand() ) );
413       }
414     }
415 #endif
416   }
417 
418   // create histogram widget
419   mHistogramWidget = nullptr;
420   if ( mOptsPage_Histogram->isEnabled() )
421   {
422     mHistogramWidget = new QgsRasterHistogramWidget( mRasterLayer, mOptsPage_Histogram );
423     mHistogramStackedWidget->addWidget( mHistogramWidget );
424   }
425 
426   //insert renderer widgets into registry
427   QgsApplication::rasterRendererRegistry()->insertWidgetFunction( QStringLiteral( "paletted" ), QgsPalettedRendererWidget::create );
428   QgsApplication::rasterRendererRegistry()->insertWidgetFunction( QStringLiteral( "multibandcolor" ), QgsMultiBandColorRendererWidget::create );
429   QgsApplication::rasterRendererRegistry()->insertWidgetFunction( QStringLiteral( "singlebandpseudocolor" ), QgsSingleBandPseudoColorRendererWidget::create );
430   QgsApplication::rasterRendererRegistry()->insertWidgetFunction( QStringLiteral( "singlebandgray" ), QgsSingleBandGrayRendererWidget::create );
431   QgsApplication::rasterRendererRegistry()->insertWidgetFunction( QStringLiteral( "hillshade" ), QgsHillshadeRendererWidget::create );
432   QgsApplication::rasterRendererRegistry()->insertWidgetFunction( QStringLiteral( "contour" ), QgsRasterContourRendererWidget::create );
433 
434   //fill available renderers into combo box
435   QgsRasterRendererRegistryEntry entry;
436   mDisableRenderTypeComboBoxCurrentIndexChanged = true;
437   const auto constRenderersList = QgsApplication::rasterRendererRegistry()->renderersList();
438   for ( const QString &name : constRenderersList )
439   {
440     if ( QgsApplication::rasterRendererRegistry()->rendererData( name, entry ) )
441     {
442       if ( ( mRasterLayer->rasterType() != QgsRasterLayer::ColorLayer && entry.name != QLatin1String( "singlebandcolordata" ) ) ||
443            ( mRasterLayer->rasterType() == QgsRasterLayer::ColorLayer && entry.name == QLatin1String( "singlebandcolordata" ) ) )
444       {
445         mRenderTypeComboBox->addItem( entry.visibleName, entry.name );
446       }
447     }
448   }
449   mDisableRenderTypeComboBoxCurrentIndexChanged = false;
450 
451   int widgetIndex = 0;
452   if ( renderer )
453   {
454     QString rendererType = renderer->type();
455     widgetIndex = mRenderTypeComboBox->findData( rendererType );
456     if ( widgetIndex != -1 )
457     {
458       mDisableRenderTypeComboBoxCurrentIndexChanged = true;
459       mRenderTypeComboBox->setCurrentIndex( widgetIndex );
460       mDisableRenderTypeComboBoxCurrentIndexChanged = false;
461     }
462 
463     if ( rendererType == QLatin1String( "singlebandcolordata" ) && mRenderTypeComboBox->count() == 1 )
464     {
465       // no band rendering options for singlebandcolordata, so minimize group box
466       QSizePolicy sizep = mBandRenderingGrpBx->sizePolicy();
467       sizep.setVerticalStretch( 0 );
468       sizep.setVerticalPolicy( QSizePolicy::Maximum );
469       mBandRenderingGrpBx->setSizePolicy( sizep );
470       mBandRenderingGrpBx->updateGeometry();
471     }
472 
473     if ( mRasterLayer->providerType() != QLatin1String( "wms" ) )
474     {
475       mWMSPrintGroupBox->hide();
476       mPublishDataSourceUrlCheckBox->hide();
477       mBackgroundLayerCheckBox->hide();
478     }
479   }
480 
481 #ifdef WITH_QTWEBKIT
482   // Setup information tab
483 
484 #if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
485   const int horizontalDpi = qApp->desktop()->screen()->logicalDpiX();
486 #else
487   const int horizontalDpi = logicalDpiX();
488 #endif
489 
490   // Adjust zoom: text is ok, but HTML seems rather big at least on Linux/KDE
491   if ( horizontalDpi > 96 )
492   {
493     mMetadataViewer->setZoomFactor( mMetadataViewer->zoomFactor() * 0.9 );
494   }
495   mMetadataViewer->page()->setLinkDelegationPolicy( QWebPage::LinkDelegationPolicy::DelegateAllLinks );
496   connect( mMetadataViewer->page(), &QWebPage::linkClicked, this, &QgsRasterLayerProperties::urlClicked );
497   mMetadataViewer->page()->settings()->setAttribute( QWebSettings::DeveloperExtrasEnabled, true );
498   mMetadataViewer->page()->settings()->setAttribute( QWebSettings::JavascriptEnabled, true );
499 
500 #endif
501 
502   mRenderTypeComboBox_currentIndexChanged( widgetIndex );
503 
504   // update based on lyr's current state
505   sync();
506 
507   QgsSettings settings;
508   // if dialog hasn't been opened/closed yet, default to Styles tab, which is used most often
509   // this will be read by restoreOptionsBaseUi()
510   if ( !settings.contains( QStringLiteral( "/Windows/RasterLayerProperties/tab" ) ) )
511   {
512     settings.setValue( QStringLiteral( "Windows/RasterLayerProperties/tab" ),
513                        mOptStackedWidget->indexOf( mOptsPage_Style ) );
514   }
515 
516   mResetColorRenderingBtn->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionUndo.svg" ) ) );
517 
518   QString title = tr( "Layer Properties — %1" ).arg( lyr->name() );
519 
520   if ( !mRasterLayer->styleManager()->isDefault( mRasterLayer->styleManager()->currentStyle() ) )
521     title += QStringLiteral( " (%1)" ).arg( mRasterLayer->styleManager()->currentStyle() );
522   restoreOptionsBaseUi( title );
523   optionsStackedWidget_CurrentChanged( mOptionsStackedWidget->currentIndex() );
524 
525   //Add help page references
526   mOptsPage_Information->setProperty( "helpPage", QStringLiteral( "working_with_raster/raster_properties.html#information-properties" ) );
527   mOptsPage_Source->setProperty( "helpPage", QStringLiteral( "working_with_raster/raster_properties.html#source-properties" ) );
528   mOptsPage_Style->setProperty( "helpPage", QStringLiteral( "working_with_raster/raster_properties.html#symbology-properties" ) );
529   mOptsPage_Transparency->setProperty( "helpPage", QStringLiteral( "working_with_raster/raster_properties.html#transparency-properties" ) );
530 
531   if ( mOptsPage_Histogram )
532     mOptsPage_Histogram->setProperty( "helpPage", QStringLiteral( "working_with_raster/raster_properties.html#histogram-properties" ) );
533 
534   mOptsPage_Rendering->setProperty( "helpPage", QStringLiteral( "working_with_raster/raster_properties.html#rendering-properties" ) );
535 
536   if ( mOptsPage_Pyramids )
537     mOptsPage_Pyramids->setProperty( "helpPage", QStringLiteral( "working_with_raster/raster_properties.html#pyramids-properties" ) );
538 
539   mOptsPage_Metadata->setProperty( "helpPage", QStringLiteral( "working_with_raster/raster_properties.html#metadata-properties" ) );
540   mOptsPage_Legend->setProperty( "helpPage", QStringLiteral( "working_with_raster/raster_properties.html#legend-properties" ) );
541   mOptsPage_Server->setProperty( "helpPage", QStringLiteral( "working_with_raster/raster_properties.html#server-properties" ) );
542 }
543 
setupTransparencyTable(int nBands)544 void QgsRasterLayerProperties::setupTransparencyTable( int nBands )
545 {
546   tableTransparency->clear();
547   tableTransparency->setColumnCount( 0 );
548   tableTransparency->setRowCount( 0 );
549   mTransparencyToEdited.clear();
550 
551   if ( nBands == 3 )
552   {
553     tableTransparency->setColumnCount( 4 );
554     tableTransparency->setHorizontalHeaderItem( 0, new QTableWidgetItem( tr( "Red" ) ) );
555     tableTransparency->setHorizontalHeaderItem( 1, new QTableWidgetItem( tr( "Green" ) ) );
556     tableTransparency->setHorizontalHeaderItem( 2, new QTableWidgetItem( tr( "Blue" ) ) );
557     tableTransparency->setHorizontalHeaderItem( 3, new QTableWidgetItem( tr( "Percent Transparent" ) ) );
558   }
559   else //1 band
560   {
561     tableTransparency->setColumnCount( 3 );
562 // Is it important to distinguish the header? It becomes difficult with range.
563 #if 0
564     if ( QgsRasterLayer::PalettedColor != mRasterLayer->drawingStyle() &&
565          QgsRasterLayer::PalettedSingleBandGray != mRasterLayer->drawingStyle() &&
566          QgsRasterLayer::PalettedSingleBandPseudoColor != mRasterLayer->drawingStyle() &&
567          QgsRasterLayer::PalettedMultiBandColor != mRasterLayer->drawingStyle() )
568     {
569       tableTransparency->setHorizontalHeaderItem( 0, new QTableWidgetItem( tr( "Gray" ) ) );
570     }
571     else
572     {
573       tableTransparency->setHorizontalHeaderItem( 0, new QTableWidgetItem( tr( "Indexed Value" ) ) );
574     }
575 #endif
576     tableTransparency->setHorizontalHeaderItem( 0, new QTableWidgetItem( tr( "From" ) ) );
577     tableTransparency->setHorizontalHeaderItem( 1, new QTableWidgetItem( tr( "To" ) ) );
578     tableTransparency->setHorizontalHeaderItem( 2, new QTableWidgetItem( tr( "Percent Transparent" ) ) );
579   }
580 
581   tableTransparency->horizontalHeader()->setSectionResizeMode( 0, QHeaderView::Stretch );
582   tableTransparency->horizontalHeader()->setSectionResizeMode( 1, QHeaderView::Stretch );
583 }
584 
populateTransparencyTable(QgsRasterRenderer * renderer)585 void QgsRasterLayerProperties::populateTransparencyTable( QgsRasterRenderer *renderer )
586 {
587   if ( !mRasterLayer )
588   {
589     return;
590   }
591 
592   if ( !renderer )
593   {
594     return;
595   }
596 
597   int nBands = renderer->usesBands().size();
598   setupTransparencyTable( nBands );
599 
600   const QgsRasterTransparency *rasterTransparency = renderer->rasterTransparency();
601   if ( !rasterTransparency )
602   {
603     return;
604   }
605 
606   if ( nBands == 1 )
607   {
608     QList<QgsRasterTransparency::TransparentSingleValuePixel> pixelList = rasterTransparency->transparentSingleValuePixelList();
609     for ( int i = 0; i < pixelList.size(); ++i )
610     {
611       tableTransparency->insertRow( i );
612       setTransparencyCell( i, 0, pixelList[i].min );
613       setTransparencyCell( i, 1, pixelList[i].max );
614       setTransparencyCell( i, 2, pixelList[i].percentTransparent );
615       // break synchronization only if values differ
616       if ( pixelList[i].min != pixelList[i].max )
617       {
618         setTransparencyToEdited( i );
619       }
620     }
621   }
622   else if ( nBands == 3 )
623   {
624     QList<QgsRasterTransparency::TransparentThreeValuePixel> pixelList = rasterTransparency->transparentThreeValuePixelList();
625     for ( int i = 0; i < pixelList.size(); ++i )
626     {
627       tableTransparency->insertRow( i );
628       setTransparencyCell( i, 0, pixelList[i].red );
629       setTransparencyCell( i, 1, pixelList[i].green );
630       setTransparencyCell( i, 2, pixelList[i].blue );
631       setTransparencyCell( i, 3, pixelList[i].percentTransparent );
632     }
633   }
634 
635   tableTransparency->resizeColumnsToContents();
636   tableTransparency->resizeRowsToContents();
637 }
638 
setRendererWidget(const QString & rendererName)639 void QgsRasterLayerProperties::setRendererWidget( const QString &rendererName )
640 {
641   QgsDebugMsg( "rendererName = " + rendererName );
642   QgsRasterRendererWidget *oldWidget = mRendererWidget;
643   QgsRasterRenderer *oldRenderer = mRasterLayer->renderer();
644 
645   int alphaBand = -1;
646   double opacity = 1;
647   QColor nodataColor;
648   if ( oldRenderer )
649   {
650     // Retain alpha band and opacity when switching renderer
651     alphaBand = oldRenderer->alphaBand();
652     opacity = oldRenderer->opacity();
653     nodataColor = oldRenderer->nodataColor();
654   }
655 
656   QgsRasterRendererRegistryEntry rendererEntry;
657   if ( QgsApplication::rasterRendererRegistry()->rendererData( rendererName, rendererEntry ) )
658   {
659     if ( rendererEntry.widgetCreateFunction ) //single band color data renderer e.g. has no widget
660     {
661       QgsDebugMsg( QStringLiteral( "renderer has widgetCreateFunction" ) );
662       // Current canvas extent (used to calc min/max) in layer CRS
663       QgsRectangle myExtent = mMapCanvas->mapSettings().outputExtentToLayerExtent( mRasterLayer, mMapCanvas->extent() );
664       if ( oldWidget && ( !oldRenderer || rendererName != oldRenderer->type() ) )
665       {
666         if ( rendererName == QLatin1String( "singlebandgray" ) )
667         {
668           whileBlocking( mRasterLayer )->setRenderer( QgsApplication::rasterRendererRegistry()->defaultRendererForDrawingStyle( QgsRaster::SingleBandGray, mRasterLayer->dataProvider() ) );
669           whileBlocking( mRasterLayer )->setDefaultContrastEnhancement();
670         }
671         else if ( rendererName == QLatin1String( "multibandcolor" ) )
672         {
673           whileBlocking( mRasterLayer )->setRenderer( QgsApplication::rasterRendererRegistry()->defaultRendererForDrawingStyle( QgsRaster::MultiBandColor, mRasterLayer->dataProvider() ) );
674           whileBlocking( mRasterLayer )->setDefaultContrastEnhancement();
675         }
676       }
677       mRasterLayer->renderer()->setAlphaBand( alphaBand );
678       mRasterLayer->renderer()->setOpacity( opacity );
679       mRasterLayer->renderer()->setNodataColor( nodataColor );
680       mRendererWidget = rendererEntry.widgetCreateFunction( mRasterLayer, myExtent );
681       mRendererWidget->setMapCanvas( mMapCanvas );
682       mRendererStackedWidget->addWidget( mRendererWidget );
683       if ( oldWidget )
684       {
685         //compare used bands in new and old renderer and reset transparency dialog if different
686         QgsRasterRenderer *oldRenderer = oldWidget->renderer();
687         QgsRasterRenderer *newRenderer = mRendererWidget->renderer();
688         QList<int> oldBands = oldRenderer->usesBands();
689         QList<int> newBands = newRenderer->usesBands();
690         if ( oldBands != newBands )
691         {
692           populateTransparencyTable( newRenderer );
693         }
694         delete oldRenderer;
695         delete newRenderer;
696       }
697     }
698   }
699 
700   const int widgetIndex = mRenderTypeComboBox->findData( rendererName );
701   if ( widgetIndex != -1 )
702   {
703     mDisableRenderTypeComboBoxCurrentIndexChanged = true;
704     mRenderTypeComboBox->setCurrentIndex( widgetIndex );
705     mDisableRenderTypeComboBoxCurrentIndexChanged = false;
706   }
707 
708   if ( mRendererWidget != oldWidget )
709     delete oldWidget;
710 
711   if ( mHistogramWidget )
712   {
713     mHistogramWidget->setRendererWidget( rendererName, mRendererWidget );
714   }
715 }
716 
717 /**
718   \note moved from ctor
719 
720   Previously this dialog was created anew with each right-click pop-up menu
721   invocation.  Changed so that the dialog always exists after first
722   invocation, and is just re-synchronized with its layer's state when
723   re-shown.
724 
725 */
sync()726 void QgsRasterLayerProperties::sync()
727 {
728   QgsSettings myQSettings;
729 
730   const QgsRasterDataProvider *provider = mRasterLayer->dataProvider();
731   if ( !provider )
732     return;
733 
734   if ( provider->dataType( 1 ) == Qgis::ARGB32
735        || provider->dataType( 1 ) == Qgis::ARGB32_Premultiplied )
736   {
737     gboxNoDataValue->setEnabled( false );
738     gboxCustomTransparency->setEnabled( false );
739     mOptionsStackedWidget->setCurrentWidget( mOptsPage_Server );
740   }
741 
742   // TODO: Wouldn't it be better to just removeWidget() the tabs than delete them? [LS]
743   if ( !( provider->capabilities() & QgsRasterDataProvider::BuildPyramids ) )
744   {
745     if ( mOptsPage_Pyramids )
746     {
747       delete mOptsPage_Pyramids;
748       mOptsPage_Pyramids = nullptr;
749     }
750   }
751 
752   if ( !( provider->capabilities() & QgsRasterDataProvider::Size ) )
753   {
754     if ( mOptsPage_Histogram )
755     {
756       delete mOptsPage_Histogram;
757       mOptsPage_Histogram = nullptr;
758       delete mHistogramWidget;
759       mHistogramWidget = nullptr;
760     }
761   }
762 
763   QgsDebugMsg( QStringLiteral( "populate transparency tab" ) );
764 
765   /*
766    * Style tab
767    */
768 
769   //set brightness, contrast and gamma
770   QgsBrightnessContrastFilter *brightnessFilter = mRasterLayer->brightnessFilter();
771   if ( brightnessFilter )
772   {
773     mSliderBrightness->setValue( brightnessFilter->brightness() );
774     mSliderContrast->setValue( brightnessFilter->contrast() );
775     mGammaSpinBox->setValue( brightnessFilter->gamma() );
776   }
777 
778   // Hue and saturation color control
779   const QgsHueSaturationFilter *hueSaturationFilter = mRasterLayer->hueSaturationFilter();
780   //set hue and saturation controls to current values
781   if ( hueSaturationFilter )
782   {
783     sliderSaturation->setValue( hueSaturationFilter->saturation() );
784     comboGrayscale->setCurrentIndex( ( int ) hueSaturationFilter->grayscaleMode() );
785 
786     // Set state of saturation controls based on grayscale mode choice
787     toggleSaturationControls( static_cast<int>( hueSaturationFilter->grayscaleMode() ) );
788 
789     // Set state of colorize controls
790     mColorizeCheck->setChecked( hueSaturationFilter->colorizeOn() );
791     btnColorizeColor->setColor( hueSaturationFilter->colorizeColor() );
792     toggleColorizeControls( hueSaturationFilter->colorizeOn() );
793     sliderColorizeStrength->setValue( hueSaturationFilter->colorizeStrength() );
794   }
795 
796   /*
797    * Transparent Pixel Tab
798    */
799 
800   //set the transparency slider
801   QgsRasterRenderer *renderer = mRasterLayer->renderer();
802   if ( renderer )
803   {
804     mOpacityWidget->setOpacity( renderer->opacity() );
805     cboxTransparencyBand->setBand( renderer->alphaBand() );
806   }
807 
808   //add current NoDataValue to NoDataValue line edit
809   // TODO: should be per band
810   // TODO: no data ranges
811   if ( provider->sourceHasNoDataValue( 1 ) )
812   {
813     double v = QgsRasterBlock::printValue( provider->sourceNoDataValue( 1 ) ).toDouble();
814     lblSrcNoDataValue->setText( QLocale().toString( v, 'g' ) );
815   }
816   else
817   {
818     lblSrcNoDataValue->setText( tr( "not defined" ) );
819   }
820 
821   mSrcNoDataValueCheckBox->setChecked( provider->useSourceNoDataValue( 1 ) );
822 
823   bool enableSrcNoData = provider->sourceHasNoDataValue( 1 ) && !std::isnan( provider->sourceNoDataValue( 1 ) );
824 
825   mSrcNoDataValueCheckBox->setEnabled( enableSrcNoData );
826   lblSrcNoDataValue->setEnabled( enableSrcNoData );
827 
828   QgsRasterRangeList noDataRangeList = provider->userNoDataValues( 1 );
829   QgsDebugMsg( QStringLiteral( "noDataRangeList.size = %1" ).arg( noDataRangeList.size() ) );
830   if ( !noDataRangeList.isEmpty() )
831   {
832     leNoDataValue->insert( QgsRasterBlock::printValue( noDataRangeList.value( 0 ).min() ) );
833   }
834   else
835   {
836     leNoDataValue->insert( QString() );
837   }
838 
839   mRefreshLayerCheckBox->setChecked( mRasterLayer->hasAutoRefreshEnabled() );
840   mRefreshLayerIntervalSpinBox->setEnabled( mRasterLayer->hasAutoRefreshEnabled() );
841   mRefreshLayerIntervalSpinBox->setValue( mRasterLayer->autoRefreshInterval() / 1000.0 );
842 
843   populateTransparencyTable( mRasterLayer->renderer() );
844 
845   QgsDebugMsg( QStringLiteral( "populate colormap tab" ) );
846   /*
847    * Transparent Pixel Tab
848    */
849 
850   QgsDebugMsg( QStringLiteral( "populate general tab" ) );
851   /*
852    * General Tab
853    */
854 
855   //these properties (layer name and label) are provided by the qgsmaplayer superclass
856   mLayerOrigNameLineEd->setText( mRasterLayer->name() );
857   leDisplayName->setText( mRasterLayer->name() );
858 
859   QgsDebugMsgLevel( QStringLiteral( "populate metadata tab" ), 2 );
860   /*
861    * Metadata Tab
862    */
863   //populate the metadata tab's text browser widget with gdal metadata info
864   updateInformationContent();
865 
866   // WMS Name as layer short name
867   mLayerShortNameLineEdit->setText( mRasterLayer->shortName() );
868   // WMS Name validator
869   QValidator *shortNameValidator = new QRegExpValidator( QgsApplication::shortNameRegExp(), this );
870   mLayerShortNameLineEdit->setValidator( shortNameValidator );
871 
872   //layer title and abstract
873   mLayerTitleLineEdit->setText( mRasterLayer->title() );
874   mLayerAbstractTextEdit->setPlainText( mRasterLayer->abstract() );
875   mLayerKeywordListLineEdit->setText( mRasterLayer->keywordList() );
876   mLayerDataUrlLineEdit->setText( mRasterLayer->dataUrl() );
877   mLayerDataUrlFormatComboBox->setCurrentIndex(
878     mLayerDataUrlFormatComboBox->findText(
879       mRasterLayer->dataUrlFormat()
880     )
881   );
882 
883   //layer attribution and metadataUrl
884   mLayerAttributionLineEdit->setText( mRasterLayer->attribution() );
885   mLayerAttributionUrlLineEdit->setText( mRasterLayer->attributionUrl() );
886   mLayerMetadataUrlLineEdit->setText( mRasterLayer->metadataUrl() );
887   mLayerMetadataUrlTypeComboBox->setCurrentIndex(
888     mLayerMetadataUrlTypeComboBox->findText(
889       mRasterLayer->metadataUrlType()
890     )
891   );
892   mLayerMetadataUrlFormatComboBox->setCurrentIndex(
893     mLayerMetadataUrlFormatComboBox->findText(
894       mRasterLayer->metadataUrlFormat()
895     )
896   );
897 
898   mLayerLegendUrlLineEdit->setText( mRasterLayer->legendUrl() );
899   mLayerLegendUrlFormatComboBox->setCurrentIndex( mLayerLegendUrlFormatComboBox->findText( mRasterLayer->legendUrlFormat() ) );
900 
901   //WMS print layer
902   QVariant wmsPrintLayer = mRasterLayer->customProperty( QStringLiteral( "WMSPrintLayer" ) );
903   if ( wmsPrintLayer.isValid() )
904   {
905     mWMSPrintLayerLineEdit->setText( wmsPrintLayer.toString() );
906   }
907 
908   QVariant wmsPublishDataSourceUrl = mRasterLayer->customProperty( QStringLiteral( "WMSPublishDataSourceUrl" ), false );
909   mPublishDataSourceUrlCheckBox->setChecked( wmsPublishDataSourceUrl.toBool() );
910 
911   QVariant wmsBackgroundLayer = mRasterLayer->customProperty( QStringLiteral( "WMSBackgroundLayer" ), false );
912   mBackgroundLayerCheckBox->setChecked( wmsBackgroundLayer.toBool() );
913 
914   mLegendConfigEmbeddedWidget->setLayer( mRasterLayer );
915 
916   mTemporalWidget->syncToLayer();
917 }
918 
apply()919 void QgsRasterLayerProperties::apply()
920 {
921 
922   // Do nothing on "bad" layers
923   if ( !mRasterLayer->isValid() )
924     return;
925 
926   /*
927    * Legend Tab
928    */
929   mLegendConfigEmbeddedWidget->applyToLayer();
930 
931   QgsDebugMsg( QStringLiteral( "apply processing symbology tab" ) );
932   /*
933    * Symbology Tab
934    */
935 
936   //set whether the layer histogram should be inverted
937   //mRasterLayer->setInvertHistogram( cboxInvertColorMap->isChecked() );
938 
939   mRasterLayer->brightnessFilter()->setBrightness( mSliderBrightness->value() );
940   mRasterLayer->brightnessFilter()->setContrast( mSliderContrast->value() );
941   mRasterLayer->brightnessFilter()->setGamma( mGammaSpinBox->value() );
942 
943   QgsDebugMsg( QStringLiteral( "processing transparency tab" ) );
944   /*
945    * Transparent Pixel Tab
946    */
947 
948   //set NoDataValue
949   QgsRasterRangeList myNoDataRangeList;
950   if ( "" != leNoDataValue->text() )
951   {
952     bool myDoubleOk = false;
953     double myNoDataValue = QgsDoubleValidator::toDouble( leNoDataValue->text(), &myDoubleOk );
954     if ( myDoubleOk )
955     {
956       QgsRasterRange myNoDataRange( myNoDataValue, myNoDataValue );
957       myNoDataRangeList << myNoDataRange;
958     }
959   }
960   for ( int bandNo = 1; bandNo <= mRasterLayer->dataProvider()->bandCount(); bandNo++ )
961   {
962     mRasterLayer->dataProvider()->setUserNoDataValue( bandNo, myNoDataRangeList );
963     mRasterLayer->dataProvider()->setUseSourceNoDataValue( bandNo, mSrcNoDataValueCheckBox->isChecked() );
964   }
965 
966   //set renderer from widget
967   QgsRasterRendererWidget *rendererWidget = dynamic_cast<QgsRasterRendererWidget *>( mRendererStackedWidget->currentWidget() );
968   if ( rendererWidget )
969   {
970     rendererWidget->doComputations();
971 
972     mRasterLayer->setRenderer( rendererWidget->renderer() );
973   }
974 
975   mMetadataWidget->acceptMetadata();
976   mMetadataFilled = false;
977 
978   //transparency settings
979   QgsRasterRenderer *rasterRenderer = mRasterLayer->renderer();
980   if ( rasterRenderer )
981   {
982     rasterRenderer->setAlphaBand( cboxTransparencyBand->currentBand() );
983 
984     //Walk through each row in table and test value. If not valid set to 0.0 and continue building transparency list
985     QgsRasterTransparency *rasterTransparency = new QgsRasterTransparency();
986     if ( tableTransparency->columnCount() == 4 )
987     {
988       QgsRasterTransparency::TransparentThreeValuePixel myTransparentPixel;
989       QList<QgsRasterTransparency::TransparentThreeValuePixel> myTransparentThreeValuePixelList;
990       for ( int myListRunner = 0; myListRunner < tableTransparency->rowCount(); myListRunner++ )
991       {
992         myTransparentPixel.red = transparencyCellValue( myListRunner, 0 );
993         myTransparentPixel.green = transparencyCellValue( myListRunner, 1 );
994         myTransparentPixel.blue = transparencyCellValue( myListRunner, 2 );
995         myTransparentPixel.percentTransparent = transparencyCellValue( myListRunner, 3 );
996         myTransparentThreeValuePixelList.append( myTransparentPixel );
997       }
998       rasterTransparency->setTransparentThreeValuePixelList( myTransparentThreeValuePixelList );
999     }
1000     else if ( tableTransparency->columnCount() == 3 )
1001     {
1002       QgsRasterTransparency::TransparentSingleValuePixel myTransparentPixel;
1003       QList<QgsRasterTransparency::TransparentSingleValuePixel> myTransparentSingleValuePixelList;
1004       for ( int myListRunner = 0; myListRunner < tableTransparency->rowCount(); myListRunner++ )
1005       {
1006         myTransparentPixel.min = transparencyCellValue( myListRunner, 0 );
1007         myTransparentPixel.max = transparencyCellValue( myListRunner, 1 );
1008         myTransparentPixel.percentTransparent = transparencyCellValue( myListRunner, 2 );
1009 
1010         myTransparentSingleValuePixelList.append( myTransparentPixel );
1011       }
1012       rasterTransparency->setTransparentSingleValuePixelList( myTransparentSingleValuePixelList );
1013     }
1014 
1015     rasterRenderer->setRasterTransparency( rasterTransparency );
1016 
1017     //set global transparency
1018     rasterRenderer->setOpacity( mOpacityWidget->opacity() );
1019   }
1020 
1021   QgsDebugMsg( QStringLiteral( "processing general tab" ) );
1022   /*
1023    * General Tab
1024    */
1025   mRasterLayer->setName( mLayerOrigNameLineEd->text() );
1026 
1027   // set up the scale based layer visibility stuff....
1028   mRasterLayer->setScaleBasedVisibility( chkUseScaleDependentRendering->isChecked() );
1029   mRasterLayer->setMinimumScale( mScaleRangeWidget->minimumScale() );
1030   mRasterLayer->setMaximumScale( mScaleRangeWidget->maximumScale() );
1031 
1032   mRasterLayer->setAutoRefreshInterval( mRefreshLayerIntervalSpinBox->value() * 1000.0 );
1033   mRasterLayer->setAutoRefreshEnabled( mRefreshLayerCheckBox->isChecked() );
1034 
1035   //update the legend pixmap
1036   // pixmapLegend->setPixmap( mRasterLayer->legendAsPixmap() );
1037   // pixmapLegend->setScaledContents( true );
1038   // pixmapLegend->repaint();
1039 
1040   mResamplingUtils.refreshLayerFromWidgets();
1041 
1042   // Hue and saturation controls
1043   QgsHueSaturationFilter *hueSaturationFilter = mRasterLayer->hueSaturationFilter();
1044   if ( hueSaturationFilter )
1045   {
1046     hueSaturationFilter->setSaturation( sliderSaturation->value() );
1047     hueSaturationFilter->setGrayscaleMode( ( QgsHueSaturationFilter::GrayscaleMode ) comboGrayscale->currentIndex() );
1048     hueSaturationFilter->setColorizeOn( mColorizeCheck->checkState() );
1049     hueSaturationFilter->setColorizeColor( btnColorizeColor->color() );
1050     hueSaturationFilter->setColorizeStrength( sliderColorizeStrength->value() );
1051   }
1052 
1053   //set the blend mode for the layer
1054   mRasterLayer->setBlendMode( mBlendModeComboBox->blendMode() );
1055 
1056   updateSourceStaticTime();
1057 
1058   // Update temporal field
1059   if ( mRasterLayer->dataProvider() )
1060   {
1061     QgsDataSourceUri uri { mRasterLayer->dataProvider()->uri() };
1062     if ( mPostgresRasterTemporalGroup->isEnabled() &&
1063          mPostgresRasterTemporalGroup->isChecked() &&
1064          ! mPostgresRasterTemporalFieldComboBox->currentField().isEmpty() )
1065     {
1066       const QString originaUri { uri.uri() };
1067       const int fieldIdx { mRasterLayer->dataProvider()->fields().lookupField( mPostgresRasterTemporalFieldComboBox->currentField() ) };
1068       uri.removeParam( QStringLiteral( "temporalFieldIndex" ) );
1069       uri.removeParam( QStringLiteral( "temporalDefaultTime" ) );
1070       if ( fieldIdx >= 0 )
1071       {
1072         uri.setParam( QStringLiteral( "temporalFieldIndex" ), QString::number( fieldIdx ) );
1073         if ( mPostgresRasterDefaultTime->dateTime().isValid() )
1074         {
1075           QDateTime defaultDateTime  { mPostgresRasterDefaultTime->dateTime() };
1076           const QTime defaultTime { defaultDateTime.time() };
1077           // Set secs to 0
1078           defaultDateTime.setTime( { defaultTime.hour(), defaultTime.minute(), 0 } );
1079           uri.setParam( QStringLiteral( "temporalDefaultTime" ), defaultDateTime.toString( Qt::DateFormat::ISODate ) );
1080         }
1081         if ( uri.uri( ) != originaUri )
1082           mRasterLayer->setDataSource( uri.uri(), mRasterLayer->name(), mRasterLayer->providerType(), QgsDataProvider::ProviderOptions() );
1083       }
1084     }
1085     else if ( uri.hasParam( QStringLiteral( "temporalFieldIndex" ) ) )
1086     {
1087       uri.removeParam( QStringLiteral( "temporalFieldIndex" ) );
1088       uri.removeParam( QStringLiteral( "temporalDefaultTime" ) );
1089       mRasterLayer->setDataSource( uri.uri(), mRasterLayer->name(), mRasterLayer->providerType(), QgsDataProvider::ProviderOptions() );
1090     }
1091   }
1092 
1093   // Update temporal properties
1094   mTemporalWidget->saveTemporalProperties();
1095 
1096   mRasterLayer->setCrs( mCrsSelector->crs() );
1097 
1098   if ( mRasterLayer->shortName() != mLayerShortNameLineEdit->text() )
1099     mMetadataFilled = false;
1100   mRasterLayer->setShortName( mLayerShortNameLineEdit->text() );
1101 
1102   if ( mRasterLayer->title() != mLayerTitleLineEdit->text() )
1103     mMetadataFilled = false;
1104   mRasterLayer->setTitle( mLayerTitleLineEdit->text() );
1105 
1106   if ( mRasterLayer->abstract() != mLayerAbstractTextEdit->toPlainText() )
1107     mMetadataFilled = false;
1108   mRasterLayer->setAbstract( mLayerAbstractTextEdit->toPlainText() );
1109 
1110   if ( mRasterLayer->keywordList() != mLayerKeywordListLineEdit->text() )
1111     mMetadataFilled = false;
1112   mRasterLayer->setKeywordList( mLayerKeywordListLineEdit->text() );
1113 
1114   if ( mRasterLayer->dataUrl() != mLayerDataUrlLineEdit->text() )
1115     mMetadataFilled = false;
1116   mRasterLayer->setDataUrl( mLayerDataUrlLineEdit->text() );
1117 
1118   if ( mRasterLayer->dataUrlFormat() != mLayerDataUrlFormatComboBox->currentText() )
1119     mMetadataFilled = false;
1120   mRasterLayer->setDataUrlFormat( mLayerDataUrlFormatComboBox->currentText() );
1121 
1122   //layer attribution and metadataUrl
1123   if ( mRasterLayer->attribution() != mLayerAttributionLineEdit->text() )
1124     mMetadataFilled = false;
1125   mRasterLayer->setAttribution( mLayerAttributionLineEdit->text() );
1126 
1127   if ( mRasterLayer->attributionUrl() != mLayerAttributionUrlLineEdit->text() )
1128     mMetadataFilled = false;
1129   mRasterLayer->setAttributionUrl( mLayerAttributionUrlLineEdit->text() );
1130 
1131   if ( mRasterLayer->metadataUrl() != mLayerMetadataUrlLineEdit->text() )
1132     mMetadataFilled = false;
1133   mRasterLayer->setMetadataUrl( mLayerMetadataUrlLineEdit->text() );
1134 
1135   if ( mRasterLayer->metadataUrlType() != mLayerMetadataUrlTypeComboBox->currentText() )
1136     mMetadataFilled = false;
1137   mRasterLayer->setMetadataUrlType( mLayerMetadataUrlTypeComboBox->currentText() );
1138 
1139   if ( mRasterLayer->metadataUrlFormat() != mLayerMetadataUrlFormatComboBox->currentText() )
1140     mMetadataFilled = false;
1141   mRasterLayer->setMetadataUrlFormat( mLayerMetadataUrlFormatComboBox->currentText() );
1142 
1143   if ( mRasterLayer->legendUrl() != mLayerLegendUrlLineEdit->text() )
1144     mMetadataFilled = false;
1145   mRasterLayer->setLegendUrl( mLayerLegendUrlLineEdit->text() );
1146 
1147   if ( mRasterLayer->legendUrlFormat() != mLayerLegendUrlFormatComboBox->currentText() )
1148     mMetadataFilled = false;
1149   mRasterLayer->setLegendUrlFormat( mLayerLegendUrlFormatComboBox->currentText() );
1150 
1151   if ( !mWMSPrintLayerLineEdit->text().isEmpty() )
1152   {
1153     mRasterLayer->setCustomProperty( QStringLiteral( "WMSPrintLayer" ), mWMSPrintLayerLineEdit->text() );
1154   }
1155 
1156   mRasterLayer->setCustomProperty( "WMSPublishDataSourceUrl", mPublishDataSourceUrlCheckBox->isChecked() );
1157   mRasterLayer->setCustomProperty( "WMSBackgroundLayer", mBackgroundLayerCheckBox->isChecked() );
1158 
1159   // Force a redraw of the legend
1160   mRasterLayer->setLegend( QgsMapLayerLegend::defaultRasterLegend( mRasterLayer ) );
1161 
1162   //make sure the layer is redrawn
1163   mRasterLayer->triggerRepaint();
1164 
1165   // notify the project we've made a change
1166   QgsProject::instance()->setDirty( true );
1167 }//apply
1168 
updateSourceStaticTime()1169 void QgsRasterLayerProperties::updateSourceStaticTime()
1170 {
1171   QgsProviderMetadata *metadata = QgsProviderRegistry::instance()->providerMetadata(
1172                                     mRasterLayer->providerType() );
1173   const QVariantMap currentUri = metadata->decodeUri( mRasterLayer->dataProvider()->dataSourceUri() );
1174 
1175   QVariantMap uri = currentUri;
1176 
1177   if ( mWmstGroup->isVisibleTo( this ) )
1178     uri[ QStringLiteral( "allowTemporalUpdates" ) ] = mWmstGroup->isChecked();
1179 
1180 
1181   if ( mWmstGroup->isEnabled() &&
1182        mRasterLayer->dataProvider() &&
1183        mRasterLayer->dataProvider()->temporalCapabilities()->hasTemporalCapabilities() )
1184   {
1185     bool enableTime = !mDisableTime->isChecked();
1186 
1187     uri[ QStringLiteral( "enableTime" ) ] = enableTime;
1188     qobject_cast< QgsRasterLayerTemporalProperties * >( mRasterLayer->temporalProperties() )->setIntervalHandlingMethod( static_cast< QgsRasterDataProviderTemporalCapabilities::IntervalHandlingMethod >(
1189           mFetchModeComboBox->currentData().toInt() ) );
1190 
1191     // Don't do static temporal updates if temporal properties are active
1192     if ( !mRasterLayer->temporalProperties()->isActive() )
1193     {
1194       if ( mStaticTemporalRange->isChecked() )
1195       {
1196         QString time = mStartStaticDateTimeEdit->dateTime().toString( Qt::ISODateWithMs ) + '/' +
1197                        mEndStaticDateTimeEdit->dateTime().toString( Qt::ISODateWithMs );
1198         uri[ QStringLiteral( "time" ) ] = time;
1199         uri[ QStringLiteral( "temporalSource" ) ] = QLatin1String( "provider" );
1200       }
1201 
1202       if ( mProjectTemporalRange->isChecked() )
1203       {
1204         QgsDateTimeRange range;
1205 
1206         if ( QgsProject::instance()->timeSettings() )
1207           range = QgsProject::instance()->timeSettings()->temporalRange();
1208         if ( range.begin().isValid() && range.end().isValid() )
1209         {
1210           QString time = range.begin().toString( Qt::ISODateWithMs ) + '/' +
1211                          range.end().toString( Qt::ISODateWithMs );
1212 
1213           uri[ QStringLiteral( "time" ) ] = time;
1214           uri[ QStringLiteral( "temporalSource" ) ] = QLatin1String( "project" );
1215         }
1216       }
1217     }
1218 
1219     if ( mReferenceTime->isChecked() )
1220     {
1221       QString referenceTime = mReferenceDateTimeEdit->dateTime().toString( Qt::ISODateWithMs );
1222       uri[ QStringLiteral( "referenceTime" ) ] = referenceTime;
1223     }
1224     else
1225     {
1226       if ( uri.contains( QStringLiteral( "referenceTime" ) ) )
1227         uri.remove( QStringLiteral( "referenceTime" ) );
1228     }
1229   }
1230 
1231   if ( currentUri != uri )
1232     mRasterLayer->setDataSource( metadata->encodeUri( uri ), mRasterLayer->name(), mRasterLayer->providerType(), QgsDataProvider::ProviderOptions() );
1233 }
1234 
setSourceStaticTimeState()1235 void QgsRasterLayerProperties::setSourceStaticTimeState()
1236 {
1237   if ( mRasterLayer->dataProvider() && mRasterLayer->dataProvider()->temporalCapabilities()->hasTemporalCapabilities() )
1238   {
1239     const QgsDateTimeRange availableProviderRange = mRasterLayer->dataProvider()->temporalCapabilities()->availableTemporalRange();
1240     const QgsDateTimeRange availableReferenceRange = mRasterLayer->dataProvider()->temporalCapabilities()->availableReferenceTemporalRange();
1241 
1242     QgsProviderMetadata *metadata = QgsProviderRegistry::instance()->providerMetadata(
1243                                       mRasterLayer->providerType() );
1244 
1245     QVariantMap uri = metadata->decodeUri( mRasterLayer->dataProvider()->dataSourceUri() );
1246 
1247     mStartStaticDateTimeEdit->setDisplayFormat( "yyyy-MM-dd HH:mm:ss" );
1248     mEndStaticDateTimeEdit->setDisplayFormat( "yyyy-MM-dd HH:mm:ss" );
1249     mReferenceDateTimeEdit->setDisplayFormat( "yyyy-MM-dd HH:mm:ss" );
1250 
1251     // setup maximum extents for widgets, based on provider's capabilities
1252     if ( availableProviderRange.begin().isValid() && availableProviderRange.end().isValid() )
1253     {
1254       mStartStaticDateTimeEdit->setDateTimeRange( availableProviderRange.begin(),
1255           availableProviderRange.end() );
1256       mStartStaticDateTimeEdit->setDateTime( availableProviderRange.begin() );
1257       mEndStaticDateTimeEdit->setDateTimeRange( availableProviderRange.begin(),
1258           availableProviderRange.end() );
1259       mEndStaticDateTimeEdit->setDateTime( availableProviderRange.end() );
1260     }
1261     if ( availableReferenceRange.begin().isValid() && availableReferenceRange.end().isValid() )
1262     {
1263       mReferenceDateTimeEdit->setDateTimeRange( availableReferenceRange.begin(),
1264           availableReferenceRange.end() );
1265       mReferenceDateTimeEdit->setDateTime( availableReferenceRange.begin() );
1266     }
1267 
1268     const QString time = uri.value( QStringLiteral( "time" ) ).toString();
1269     if ( !time.isEmpty() )
1270     {
1271       QStringList parts = time.split( '/' );
1272       mStartStaticDateTimeEdit->setDateTime( QDateTime::fromString( parts.at( 0 ), Qt::ISODateWithMs ) );
1273       mEndStaticDateTimeEdit->setDateTime( QDateTime::fromString( parts.at( 1 ), Qt::ISODateWithMs ) );
1274     }
1275 
1276     const QString referenceTimeExtent = uri.value( QStringLiteral( "referenceTimeDimensionExtent" ) ).toString();
1277 
1278     mReferenceTime->setEnabled( !referenceTimeExtent.isEmpty() );
1279     mReferenceDateTimeEdit->setVisible( !referenceTimeExtent.isEmpty() );
1280 
1281     QString referenceTimeLabelText = referenceTimeExtent.isEmpty() ?
1282                                      tr( "There is no reference time in the layer's capabilities." ) : QString();
1283     mReferenceTimeLabel->setText( referenceTimeLabelText );
1284 
1285     const QString referenceTime = uri.value( QStringLiteral( "referenceTime" ) ).toString();
1286 
1287     mReferenceTime->setChecked( !referenceTime.isEmpty() );
1288 
1289     if ( !referenceTime.isEmpty() && !referenceTimeExtent.isEmpty() )
1290     {
1291       mReferenceDateTimeEdit->setDateTime( QDateTime::fromString( referenceTime, Qt::ISODateWithMs ) );
1292     }
1293 
1294     mFetchModeComboBox->addItem( tr( "Use Whole Temporal Range" ), QgsRasterDataProviderTemporalCapabilities::MatchUsingWholeRange );
1295     mFetchModeComboBox->addItem( tr( "Match to Start of Range" ), QgsRasterDataProviderTemporalCapabilities::MatchExactUsingStartOfRange );
1296     mFetchModeComboBox->addItem( tr( "Match to End of Range" ), QgsRasterDataProviderTemporalCapabilities::MatchExactUsingEndOfRange );
1297     mFetchModeComboBox->addItem( tr( "Closest Match to Start of Range" ), QgsRasterDataProviderTemporalCapabilities::FindClosestMatchToStartOfRange );
1298     mFetchModeComboBox->addItem( tr( "Closest Match to End of Range" ), QgsRasterDataProviderTemporalCapabilities::FindClosestMatchToEndOfRange );
1299     mFetchModeComboBox->setCurrentIndex( mFetchModeComboBox->findData( qobject_cast< QgsRasterLayerTemporalProperties * >( mRasterLayer->temporalProperties() )->intervalHandlingMethod() ) );
1300 
1301     const QString temporalSource = uri.value( QStringLiteral( "temporalSource" ) ).toString();
1302     bool enableTime = uri.value( QStringLiteral( "enableTime" ), true ).toBool();
1303 
1304     if ( temporalSource == QLatin1String( "provider" ) )
1305       mStaticTemporalRange->setChecked( !time.isEmpty() );
1306     else if ( temporalSource == QLatin1String( "project" ) )
1307       mProjectTemporalRange->setChecked( !time.isEmpty() );
1308 
1309     mDisableTime->setChecked( !enableTime );
1310 
1311     mWmstOptions->setEnabled( !mRasterLayer->temporalProperties()->isActive() );
1312 
1313     if ( mRasterLayer->temporalProperties()->isActive() )
1314       mWmstOptionsLabel->setText( tr( "The static temporal options below are disabled because the layer "
1315                                       "temporal properties are active, to enable them disable temporal properties "
1316                                       "in the temporal tab. " ) );
1317     QgsDateTimeRange range;
1318     if ( QgsProject::instance()->timeSettings() )
1319       range = QgsProject::instance()->timeSettings()->temporalRange();
1320 
1321     if ( !range.begin().isValid() || !range.end().isValid() )
1322     {
1323       mProjectTemporalRange->setEnabled( false );
1324       mProjectTemporalRangeLabel->setText( tr( "The option below is disabled because the project temporal range "
1325                                            "is not valid, update the project temporal range in the project properties "
1326                                            "with valid values in order to use it here." ) );
1327     }
1328 
1329     mWmstGroup->setChecked( uri.contains( QStringLiteral( "allowTemporalUpdates" ) ) &&
1330                             uri.value( QStringLiteral( "allowTemporalUpdates" ), true ).toBool() );
1331   }
1332 }
1333 
staticTemporalRange_toggled(bool checked)1334 void QgsRasterLayerProperties::staticTemporalRange_toggled( bool checked )
1335 {
1336   if ( checked )
1337   {
1338     mLabel->clear();
1339   }
1340 }
1341 
passProjectTemporalRange_toggled(bool checked)1342 void QgsRasterLayerProperties::passProjectTemporalRange_toggled( bool checked )
1343 {
1344   if ( checked )
1345   {
1346     QgsDateTimeRange range;
1347     if ( QgsProject::instance()->timeSettings() )
1348       range = QgsProject::instance()->timeSettings()->temporalRange();
1349 
1350     if ( range.begin().isValid() && range.end().isValid() )
1351       mLabel->setText( tr( "Project temporal range is set from %1 to %2" ).arg(
1352                          range.begin().toString( "yyyy-MM-dd HH:mm:ss" ),
1353                          range.end().toString( "yyyy-MM-dd HH:mm:ss" )
1354                        ) );
1355     else
1356       mLabel->setText( tr( "Project temporal range is not valid, can't use it here" ) );
1357   }
1358 }
1359 
temporalPropertiesChange()1360 void QgsRasterLayerProperties::temporalPropertiesChange()
1361 {
1362   mWmstOptions->setEnabled( !mRasterLayer->temporalProperties()->isActive() );
1363 
1364   if ( mRasterLayer->temporalProperties()->isActive() )
1365     mWmstOptionsLabel->setText( tr( "The static temporal options below are disabled because the layer "
1366                                     "temporal properties are active, to enable them disable temporal properties "
1367                                     "in the temporal tab. " ) );
1368   else
1369     mWmstOptionsLabel->clear();
1370 }
1371 
mLayerOrigNameLineEd_textEdited(const QString & text)1372 void QgsRasterLayerProperties::mLayerOrigNameLineEd_textEdited( const QString &text )
1373 {
1374   leDisplayName->setText( mRasterLayer->formatLayerName( text ) );
1375 }
1376 
buttonBuildPyramids_clicked()1377 void QgsRasterLayerProperties::buttonBuildPyramids_clicked()
1378 {
1379   QgsRasterDataProvider *provider = mRasterLayer->dataProvider();
1380 
1381   std::unique_ptr< QgsRasterBlockFeedback > feedback( new QgsRasterBlockFeedback() );
1382 
1383   connect( feedback.get(), &QgsRasterBlockFeedback::progressChanged, mPyramidProgress, &QProgressBar::setValue );
1384   //
1385   // Go through the list marking any files that are selected in the listview
1386   // as true so that we can generate pyramids for them.
1387   //
1388   QList< QgsRasterPyramid> myPyramidList = provider->buildPyramidList();
1389   for ( int myCounterInt = 0; myCounterInt < lbxPyramidResolutions->count(); myCounterInt++ )
1390   {
1391     QListWidgetItem *myItem = lbxPyramidResolutions->item( myCounterInt );
1392     //mark to be pyramided
1393     myPyramidList[myCounterInt].build = myItem->isSelected() || myPyramidList[myCounterInt].exists;
1394   }
1395 
1396   // keep it in sync with qgsrasterpyramidsoptionwidget.cpp
1397   QString prefix = provider->name() + "/driverOptions/_pyramids/";
1398   QgsSettings mySettings;
1399   QString resamplingMethod( cboResamplingMethod->currentData().toString() );
1400   mySettings.setValue( prefix + "resampling", resamplingMethod );
1401 
1402   //
1403   // Ask raster layer to build the pyramids
1404   //
1405 
1406   // let the user know we're going to possibly be taking a while
1407   QApplication::setOverrideCursor( Qt::WaitCursor );
1408   QString res = provider->buildPyramids(
1409                   myPyramidList,
1410                   resamplingMethod,
1411                   ( QgsRaster::RasterPyramidsFormat ) cbxPyramidsFormat->currentIndex(),
1412                   QStringList(),
1413                   feedback.get() );
1414   QApplication::restoreOverrideCursor();
1415   mPyramidProgress->setValue( 0 );
1416   buttonBuildPyramids->setEnabled( false );
1417   if ( !res.isNull() )
1418   {
1419     if ( res == QLatin1String( "CANCELED" ) )
1420     {
1421       // user canceled
1422     }
1423     else if ( res == QLatin1String( "ERROR_WRITE_ACCESS" ) )
1424     {
1425       QMessageBox::warning( this, tr( "Building Pyramids" ),
1426                             tr( "Write access denied. Adjust the file permissions and try again." ) );
1427     }
1428     else if ( res == QLatin1String( "ERROR_WRITE_FORMAT" ) )
1429     {
1430       QMessageBox::warning( this, tr( "Building Pyramids" ),
1431                             tr( "The file was not writable. Some formats do not "
1432                                 "support pyramid overviews. Consult the GDAL documentation if in doubt." ) );
1433     }
1434     else if ( res == QLatin1String( "FAILED_NOT_SUPPORTED" ) )
1435     {
1436       QMessageBox::warning( this, tr( "Building Pyramids" ),
1437                             tr( "Building pyramid overviews is not supported on this type of raster." ) );
1438     }
1439     else if ( res == QLatin1String( "ERROR_JPEG_COMPRESSION" ) )
1440     {
1441       QMessageBox::warning( this, tr( "Building Pyramids" ),
1442                             tr( "Building internal pyramid overviews is not supported on raster layers with JPEG compression and your current libtiff library." ) );
1443     }
1444     else if ( res == QLatin1String( "ERROR_VIRTUAL" ) )
1445     {
1446       QMessageBox::warning( this, tr( "Building Pyramids" ),
1447                             tr( "Building pyramid overviews is not supported on this type of raster." ) );
1448     }
1449 
1450   }
1451 
1452   //
1453   // repopulate the pyramids list
1454   //
1455   lbxPyramidResolutions->clear();
1456   // Need to rebuild list as some or all pyramids may have failed to build
1457   myPyramidList = provider->buildPyramidList();
1458   QIcon myPyramidPixmap( QgsApplication::getThemeIcon( "/mIconPyramid.svg" ) );
1459   QIcon myNoPyramidPixmap( QgsApplication::getThemeIcon( "/mIconNoPyramid.svg" ) );
1460 
1461   QList< QgsRasterPyramid >::iterator myRasterPyramidIterator;
1462   for ( myRasterPyramidIterator = myPyramidList.begin();
1463         myRasterPyramidIterator != myPyramidList.end();
1464         ++myRasterPyramidIterator )
1465   {
1466     if ( myRasterPyramidIterator->exists )
1467     {
1468       lbxPyramidResolutions->addItem( new QListWidgetItem( myPyramidPixmap,
1469                                       QString::number( myRasterPyramidIterator->xDim ) + QStringLiteral( " x " ) +
1470                                       QString::number( myRasterPyramidIterator->yDim ) ) );
1471     }
1472     else
1473     {
1474       lbxPyramidResolutions->addItem( new QListWidgetItem( myNoPyramidPixmap,
1475                                       QString::number( myRasterPyramidIterator->xDim ) + QStringLiteral( " x " ) +
1476                                       QString::number( myRasterPyramidIterator->yDim ) ) );
1477     }
1478   }
1479   //update the legend pixmap
1480   // pixmapLegend->setPixmap( mRasterLayer->legendAsPixmap() );
1481   // pixmapLegend->setScaledContents( true );
1482   // pixmapLegend->repaint();
1483 
1484   //populate the metadata tab's text browser widget with gdal metadata info
1485   updateInformationContent();
1486 }
1487 
urlClicked(const QUrl & url)1488 void QgsRasterLayerProperties::urlClicked( const QUrl &url )
1489 {
1490   QFileInfo file( url.toLocalFile() );
1491   if ( file.exists() && !file.isDir() )
1492     QgsGui::instance()->nativePlatformInterface()->openFileExplorerAndSelectFile( url.toLocalFile() );
1493   else
1494     QDesktopServices::openUrl( url );
1495 }
1496 
mRenderTypeComboBox_currentIndexChanged(int index)1497 void QgsRasterLayerProperties::mRenderTypeComboBox_currentIndexChanged( int index )
1498 {
1499   if ( index < 0 || mDisableRenderTypeComboBoxCurrentIndexChanged || ! mRasterLayer->renderer() )
1500   {
1501     return;
1502   }
1503 
1504   QString rendererName = mRenderTypeComboBox->itemData( index ).toString();
1505   setRendererWidget( rendererName );
1506 }
1507 
pbnAddValuesFromDisplay_clicked()1508 void QgsRasterLayerProperties::pbnAddValuesFromDisplay_clicked()
1509 {
1510   if ( mMapCanvas && mPixelSelectorTool )
1511   {
1512     //Need to work around the modality of the dialog but can not just hide() it.
1513     // According to Qt5 docs, to change modality the dialog needs to be hidden
1514     // and shown again.
1515     hide();
1516     setModal( false );
1517 
1518     // Transfer focus to the canvas to use the selector tool
1519     mMapCanvas->window()->raise();
1520     mMapCanvas->window()->activateWindow();
1521     mMapCanvas->window()->setFocus();
1522     mMapCanvas->setMapTool( mPixelSelectorTool.get() );
1523 
1524   }
1525 }
1526 
pbnAddValuesManually_clicked()1527 void QgsRasterLayerProperties::pbnAddValuesManually_clicked()
1528 {
1529   QgsRasterRenderer *renderer = mRendererWidget->renderer();
1530   if ( !renderer )
1531   {
1532     return;
1533   }
1534 
1535   tableTransparency->insertRow( tableTransparency->rowCount() );
1536 
1537   int n = renderer->usesBands().size();
1538   if ( n == 1 ) n++;
1539 
1540   for ( int i = 0; i < n; i++ )
1541   {
1542     setTransparencyCell( tableTransparency->rowCount() - 1, i, std::numeric_limits<double>::quiet_NaN() );
1543   }
1544 
1545   setTransparencyCell( tableTransparency->rowCount() - 1, n, 100 );
1546 
1547   tableTransparency->resizeColumnsToContents();
1548   tableTransparency->resizeRowsToContents();
1549 }
1550 
mCrsSelector_crsChanged(const QgsCoordinateReferenceSystem & crs)1551 void QgsRasterLayerProperties::mCrsSelector_crsChanged( const QgsCoordinateReferenceSystem &crs )
1552 {
1553   QgsDatumTransformDialog::run( crs, QgsProject::instance()->crs(), this, mMapCanvas, tr( "Select Transformation" ) );
1554   mRasterLayer->setCrs( crs );
1555   mMetadataWidget->crsChanged();
1556 }
1557 
pbnDefaultValues_clicked()1558 void QgsRasterLayerProperties::pbnDefaultValues_clicked()
1559 {
1560   if ( !mRendererWidget )
1561   {
1562     return;
1563   }
1564 
1565   QgsRasterRenderer *r = mRendererWidget->renderer();
1566   if ( !r )
1567   {
1568     return;
1569   }
1570 
1571   int nBands = r->usesBands().size();
1572   delete r; // really delete?
1573 
1574   setupTransparencyTable( nBands );
1575 
1576   tableTransparency->resizeColumnsToContents(); // works only with values
1577   tableTransparency->resizeRowsToContents();
1578 }
1579 
setTransparencyCell(int row,int column,double value)1580 void QgsRasterLayerProperties::setTransparencyCell( int row, int column, double value )
1581 {
1582   QgsDebugMsg( QStringLiteral( "value = %1" ).arg( value, 0, 'g', 17 ) );
1583   QgsRasterDataProvider *provider = mRasterLayer->dataProvider();
1584   if ( !provider ) return;
1585 
1586   QgsRasterRenderer *renderer = mRendererWidget->renderer();
1587   if ( !renderer ) return;
1588   int nBands = renderer->usesBands().size();
1589 
1590   QLineEdit *lineEdit = new QLineEdit();
1591   lineEdit->setFrame( false ); // frame looks bad in table
1592   // Without margins row selection is not displayed (important for delete row)
1593   lineEdit->setContentsMargins( 1, 1, 1, 1 );
1594 
1595   if ( column == tableTransparency->columnCount() - 1 )
1596   {
1597     // transparency
1598     // Who needs transparency as floating point?
1599     lineEdit->setValidator( new QIntValidator( nullptr ) );
1600     lineEdit->setText( QString::number( static_cast<int>( value ) ) );
1601   }
1602   else
1603   {
1604     // value
1605     QString valueString;
1606     switch ( provider->sourceDataType( 1 ) )
1607     {
1608       case Qgis::Float32:
1609       case Qgis::Float64:
1610         lineEdit->setValidator( new QgsDoubleValidator( nullptr ) );
1611         if ( !std::isnan( value ) )
1612         {
1613           double v = QgsRasterBlock::printValue( value ).toDouble();
1614           valueString = QLocale().toString( v, 'g' ) ;
1615         }
1616         break;
1617       default:
1618         lineEdit->setValidator( new QIntValidator( nullptr ) );
1619         if ( !std::isnan( value ) )
1620         {
1621           valueString = QLocale().toString( static_cast<int>( value ) );
1622         }
1623         break;
1624     }
1625     lineEdit->setText( valueString );
1626   }
1627   tableTransparency->setCellWidget( row, column, lineEdit );
1628   adjustTransparencyCellWidth( row, column );
1629 
1630   if ( nBands == 1 && ( column == 0 || column == 1 ) )
1631   {
1632     connect( lineEdit, &QLineEdit::textEdited, this, &QgsRasterLayerProperties::transparencyCellTextEdited );
1633   }
1634   tableTransparency->resizeColumnsToContents();
1635 }
1636 
setTransparencyCellValue(int row,int column,double value)1637 void QgsRasterLayerProperties::setTransparencyCellValue( int row, int column, double value )
1638 {
1639   QLineEdit *lineEdit = dynamic_cast<QLineEdit *>( tableTransparency->cellWidget( row, column ) );
1640   if ( !lineEdit ) return;
1641   double v = QgsRasterBlock::printValue( value ).toDouble();
1642   lineEdit->setText( QLocale().toString( v, 'g' ) );
1643   lineEdit->adjustSize();
1644   adjustTransparencyCellWidth( row, column );
1645   tableTransparency->resizeColumnsToContents();
1646 }
1647 
transparencyCellValue(int row,int column)1648 double QgsRasterLayerProperties::transparencyCellValue( int row, int column )
1649 {
1650   QLineEdit *lineEdit = dynamic_cast<QLineEdit *>( tableTransparency->cellWidget( row, column ) );
1651   if ( !lineEdit || lineEdit->text().isEmpty() )
1652   {
1653     return std::numeric_limits<double>::quiet_NaN();
1654   }
1655   return lineEdit->text().toDouble();
1656 }
1657 
adjustTransparencyCellWidth(int row,int column)1658 void QgsRasterLayerProperties::adjustTransparencyCellWidth( int row, int column )
1659 {
1660   QLineEdit *lineEdit = dynamic_cast<QLineEdit *>( tableTransparency->cellWidget( row, column ) );
1661   if ( !lineEdit ) return;
1662 
1663   int width = std::max( lineEdit->fontMetrics().boundingRect( lineEdit->text() ).width() + 10, 100 );
1664   width = std::max( width, tableTransparency->columnWidth( column ) );
1665 
1666   lineEdit->setFixedWidth( width );
1667 }
1668 
pbnExportTransparentPixelValues_clicked()1669 void QgsRasterLayerProperties::pbnExportTransparentPixelValues_clicked()
1670 {
1671   QgsSettings myQSettings;
1672   QString myLastDir = myQSettings.value( QStringLiteral( "lastRasterFileFilterDir" ), QDir::homePath() ).toString();
1673   QString myFileName = QFileDialog::getSaveFileName( this, tr( "Save File" ), myLastDir, tr( "Textfile" ) + " (*.txt)" );
1674   if ( !myFileName.isEmpty() )
1675   {
1676     if ( !myFileName.endsWith( QLatin1String( ".txt" ), Qt::CaseInsensitive ) )
1677     {
1678       myFileName = myFileName + ".txt";
1679     }
1680 
1681     QFile myOutputFile( myFileName );
1682     if ( myOutputFile.open( QFile::WriteOnly | QIODevice::Truncate ) )
1683     {
1684       QTextStream myOutputStream( &myOutputFile );
1685       myOutputStream << "# " << tr( "QGIS Generated Transparent Pixel Value Export File" ) << '\n';
1686       if ( rasterIsMultiBandColor() )
1687       {
1688         myOutputStream << "#\n#\n# " << tr( "Red" ) << "\t" << tr( "Green" ) << "\t" << tr( "Blue" ) << "\t" << tr( "Percent Transparent" );
1689         for ( int myTableRunner = 0; myTableRunner < tableTransparency->rowCount(); myTableRunner++ )
1690         {
1691           myOutputStream << '\n' << QString::number( transparencyCellValue( myTableRunner, 0 ) ) << "\t"
1692                          << QString::number( transparencyCellValue( myTableRunner, 1 ) ) << "\t"
1693                          << QString::number( transparencyCellValue( myTableRunner, 2 ) ) << "\t"
1694                          << QString::number( transparencyCellValue( myTableRunner, 3 ) );
1695         }
1696       }
1697       else
1698       {
1699         myOutputStream << "#\n#\n# " << tr( "Value" ) << "\t" << tr( "Percent Transparent" );
1700 
1701         for ( int myTableRunner = 0; myTableRunner < tableTransparency->rowCount(); myTableRunner++ )
1702         {
1703           myOutputStream << '\n' << QString::number( transparencyCellValue( myTableRunner, 0 ) ) << "\t"
1704                          << QString::number( transparencyCellValue( myTableRunner, 1 ) ) << "\t"
1705                          << QString::number( transparencyCellValue( myTableRunner, 2 ) );
1706         }
1707       }
1708     }
1709     else
1710     {
1711       QMessageBox::warning( this, tr( "Export Transparent Pixels" ), tr( "Write access denied. Adjust the file permissions and try again.\n\n" ) );
1712     }
1713   }
1714 }
1715 
transparencyCellTextEdited(const QString & text)1716 void QgsRasterLayerProperties::transparencyCellTextEdited( const QString &text )
1717 {
1718   Q_UNUSED( text )
1719   QgsDebugMsg( QStringLiteral( "text = %1" ).arg( text ) );
1720   QgsRasterRenderer *renderer = mRendererWidget->renderer();
1721   if ( !renderer )
1722   {
1723     return;
1724   }
1725   int nBands = renderer->usesBands().size();
1726   if ( nBands == 1 )
1727   {
1728     QLineEdit *lineEdit = qobject_cast<QLineEdit *>( sender() );
1729     if ( !lineEdit ) return;
1730     int row = -1;
1731     int column = -1;
1732     for ( int r = 0; r < tableTransparency->rowCount(); r++ )
1733     {
1734       for ( int c = 0; c < tableTransparency->columnCount(); c++ )
1735       {
1736         if ( tableTransparency->cellWidget( r, c ) == sender() )
1737         {
1738           row = r;
1739           column = c;
1740           break;
1741         }
1742       }
1743       if ( row != -1 ) break;
1744     }
1745     QgsDebugMsg( QStringLiteral( "row = %1 column =%2" ).arg( row ).arg( column ) );
1746 
1747     if ( column == 0 )
1748     {
1749       QLineEdit *toLineEdit = dynamic_cast<QLineEdit *>( tableTransparency->cellWidget( row, 1 ) );
1750       if ( !toLineEdit ) return;
1751       bool toChanged = mTransparencyToEdited.value( row );
1752       QgsDebugMsg( QStringLiteral( "toChanged = %1" ).arg( toChanged ) );
1753       if ( !toChanged )
1754       {
1755         toLineEdit->setText( lineEdit->text() );
1756       }
1757     }
1758     else if ( column == 1 )
1759     {
1760       setTransparencyToEdited( row );
1761     }
1762   }
1763 }
1764 
aboutToShowStyleMenu()1765 void QgsRasterLayerProperties::aboutToShowStyleMenu()
1766 {
1767   // this should be unified with QgsVectorLayerProperties::aboutToShowStyleMenu()
1768 
1769   QMenu *m = qobject_cast<QMenu *>( sender() );
1770 
1771   QgsMapLayerStyleGuiUtils::instance()->removesExtraMenuSeparators( m );
1772   // re-add style manager actions!
1773   m->addSeparator();
1774   QgsMapLayerStyleGuiUtils::instance()->addStyleManagerActions( m, mRasterLayer );
1775 }
1776 
syncToLayer()1777 void QgsRasterLayerProperties::syncToLayer()
1778 {
1779   QgsRasterRenderer *renderer = mRasterLayer->renderer();
1780   if ( renderer )
1781   {
1782     setRendererWidget( renderer->type() );
1783   }
1784   sync();
1785   mRasterLayer->triggerRepaint();
1786 }
1787 
setTransparencyToEdited(int row)1788 void QgsRasterLayerProperties::setTransparencyToEdited( int row )
1789 {
1790   if ( row >= mTransparencyToEdited.size() )
1791   {
1792     mTransparencyToEdited.resize( row + 1 );
1793   }
1794   mTransparencyToEdited[row] = true;
1795 }
1796 
optionsStackedWidget_CurrentChanged(int index)1797 void QgsRasterLayerProperties::optionsStackedWidget_CurrentChanged( int index )
1798 {
1799   QgsOptionsDialogBase::optionsStackedWidget_CurrentChanged( index );
1800 
1801   bool isMetadataPanel = ( index == mOptStackedWidget->indexOf( mOptsPage_Metadata ) );
1802   mBtnStyle->setVisible( ! isMetadataPanel );
1803   mBtnMetadata->setVisible( isMetadataPanel );
1804 
1805   if ( !mHistogramWidget )
1806     return;
1807 
1808   if ( index == mOptStackedWidget->indexOf( mOptsPage_Histogram ) )
1809   {
1810     mHistogramWidget->setActive( true );
1811   }
1812   else
1813   {
1814     mHistogramWidget->setActive( false );
1815   }
1816 
1817   if ( index == mOptStackedWidget->indexOf( mOptsPage_Information ) || !mMetadataFilled )
1818   {
1819     //set the metadata contents (which can be expensive)
1820     updateInformationContent();
1821   }
1822 }
1823 
setEndAsStartStaticButton_clicked()1824 void QgsRasterLayerProperties::setEndAsStartStaticButton_clicked()
1825 {
1826   mEndStaticDateTimeEdit->setDateTime( mStartStaticDateTimeEdit->dateTime() );
1827 }
1828 
pbnImportTransparentPixelValues_clicked()1829 void QgsRasterLayerProperties::pbnImportTransparentPixelValues_clicked()
1830 {
1831   int myLineCounter = 0;
1832   bool myImportError = false;
1833   QString myBadLines;
1834   QgsSettings myQSettings;
1835   QString myLastDir = myQSettings.value( QStringLiteral( "lastRasterFileFilterDir" ), QDir::homePath() ).toString();
1836   QString myFileName = QFileDialog::getOpenFileName( this, tr( "Open file" ), myLastDir, tr( "Textfile" ) + " (*.txt)" );
1837   QFile myInputFile( myFileName );
1838   if ( myInputFile.open( QFile::ReadOnly ) )
1839   {
1840     QTextStream myInputStream( &myInputFile );
1841     QString myInputLine;
1842     if ( rasterIsMultiBandColor() )
1843     {
1844       for ( int myTableRunner = tableTransparency->rowCount() - 1; myTableRunner >= 0; myTableRunner-- )
1845       {
1846         tableTransparency->removeRow( myTableRunner );
1847       }
1848 
1849       while ( !myInputStream.atEnd() )
1850       {
1851         myLineCounter++;
1852         myInputLine = myInputStream.readLine();
1853         if ( !myInputLine.isEmpty() )
1854         {
1855           if ( !myInputLine.simplified().startsWith( '#' ) )
1856           {
1857             QStringList myTokens = myInputLine.split( QRegExp( "\\s+" ), QString::SkipEmptyParts );
1858             if ( myTokens.count() != 4 )
1859             {
1860               myImportError = true;
1861               myBadLines = myBadLines + QString::number( myLineCounter ) + ":\t[" + myInputLine + "]\n";
1862             }
1863             else
1864             {
1865               tableTransparency->insertRow( tableTransparency->rowCount() );
1866               for ( int col = 0; col < 4; col++ )
1867               {
1868                 setTransparencyCell( tableTransparency->rowCount() - 1, col, myTokens[col].toDouble() );
1869               }
1870             }
1871           }
1872         }
1873       }
1874     }
1875     else
1876     {
1877       for ( int myTableRunner = tableTransparency->rowCount() - 1; myTableRunner >= 0; myTableRunner-- )
1878       {
1879         tableTransparency->removeRow( myTableRunner );
1880       }
1881 
1882       while ( !myInputStream.atEnd() )
1883       {
1884         myLineCounter++;
1885         myInputLine = myInputStream.readLine();
1886         if ( !myInputLine.isEmpty() )
1887         {
1888           if ( !myInputLine.simplified().startsWith( '#' ) )
1889           {
1890             QStringList myTokens = myInputLine.split( QRegExp( "\\s+" ), QString::SkipEmptyParts );
1891             if ( myTokens.count() != 3 && myTokens.count() != 2 ) // 2 for QGIS < 1.9 compatibility
1892             {
1893               myImportError = true;
1894               myBadLines = myBadLines + QString::number( myLineCounter ) + ":\t[" + myInputLine + "]\n";
1895             }
1896             else
1897             {
1898               if ( myTokens.count() == 2 )
1899               {
1900                 myTokens.insert( 1, myTokens[0] ); // add 'to' value, QGIS < 1.9 compatibility
1901               }
1902               tableTransparency->insertRow( tableTransparency->rowCount() );
1903               for ( int col = 0; col < 3; col++ )
1904               {
1905                 setTransparencyCell( tableTransparency->rowCount() - 1, col, myTokens[col].toDouble() );
1906               }
1907             }
1908           }
1909         }
1910       }
1911     }
1912 
1913     if ( myImportError )
1914     {
1915       QMessageBox::warning( this, tr( "Import Transparent Pixels" ), tr( "The following lines contained errors\n\n%1" ).arg( myBadLines ) );
1916     }
1917   }
1918   else if ( !myFileName.isEmpty() )
1919   {
1920     QMessageBox::warning( this, tr( "Import Transparent Pixels" ), tr( "Read access denied. Adjust the file permissions and try again.\n\n" ) );
1921   }
1922   tableTransparency->resizeColumnsToContents();
1923   tableTransparency->resizeRowsToContents();
1924 }
1925 
pbnRemoveSelectedRow_clicked()1926 void QgsRasterLayerProperties::pbnRemoveSelectedRow_clicked()
1927 {
1928   if ( 0 < tableTransparency->rowCount() )
1929   {
1930     tableTransparency->removeRow( tableTransparency->currentRow() );
1931   }
1932 }
1933 
pixelSelected(const QgsPointXY & canvasPoint,const Qt::MouseButton & btn)1934 void QgsRasterLayerProperties::pixelSelected( const QgsPointXY &canvasPoint, const Qt::MouseButton &btn )
1935 {
1936   Q_UNUSED( btn )
1937   QgsRasterRenderer *renderer = mRendererWidget->renderer();
1938   if ( !renderer )
1939   {
1940     return;
1941   }
1942 
1943   //Get the pixel values and add a new entry to the transparency table
1944   if ( mMapCanvas && mPixelSelectorTool )
1945   {
1946     mMapCanvas->unsetMapTool( mPixelSelectorTool.get() );
1947 
1948     const QgsMapSettings &ms = mMapCanvas->mapSettings();
1949     QgsPointXY myPoint = ms.mapToLayerCoordinates( mRasterLayer, canvasPoint );
1950 
1951     QgsRectangle myExtent = ms.mapToLayerCoordinates( mRasterLayer, mMapCanvas->extent() );
1952     double mapUnitsPerPixel = mMapCanvas->mapUnitsPerPixel();
1953     int myWidth = mMapCanvas->extent().width() / mapUnitsPerPixel;
1954     int myHeight = mMapCanvas->extent().height() / mapUnitsPerPixel;
1955 
1956     QMap<int, QVariant> myPixelMap = mRasterLayer->dataProvider()->identify( myPoint, QgsRaster::IdentifyFormatValue, myExtent, myWidth, myHeight ).results();
1957 
1958     QList<int> bands = renderer->usesBands();
1959 
1960     QList<double> values;
1961     for ( int i = 0; i < bands.size(); ++i )
1962     {
1963       int bandNo = bands.value( i );
1964       if ( myPixelMap.count( bandNo ) == 1 )
1965       {
1966         if ( myPixelMap.value( bandNo ).isNull() )
1967         {
1968           return; // Don't add nodata, transparent anyway
1969         }
1970         double value = myPixelMap.value( bandNo ).toDouble();
1971         QgsDebugMsg( QStringLiteral( "value = %1" ).arg( value, 0, 'g', 17 ) );
1972         values.append( value );
1973       }
1974     }
1975     if ( bands.size() == 1 )
1976     {
1977       // Set 'to'
1978       values.insert( 1, values.value( 0 ) );
1979     }
1980     tableTransparency->insertRow( tableTransparency->rowCount() );
1981     for ( int i = 0; i < values.size(); i++ )
1982     {
1983       setTransparencyCell( tableTransparency->rowCount() - 1, i, values.value( i ) );
1984     }
1985     setTransparencyCell( tableTransparency->rowCount() - 1, tableTransparency->columnCount() - 1, 100 );
1986   }
1987   delete renderer;
1988 
1989   tableTransparency->resizeColumnsToContents();
1990   tableTransparency->resizeRowsToContents();
1991 }
1992 
toggleSaturationControls(int grayscaleMode)1993 void QgsRasterLayerProperties::toggleSaturationControls( int grayscaleMode )
1994 {
1995   // Enable or disable saturation controls based on choice of grayscale mode
1996   if ( grayscaleMode == 0 )
1997   {
1998     sliderSaturation->setEnabled( true );
1999     spinBoxSaturation->setEnabled( true );
2000   }
2001   else
2002   {
2003     sliderSaturation->setEnabled( false );
2004     spinBoxSaturation->setEnabled( false );
2005   }
2006 }
2007 
toggleColorizeControls(bool colorizeEnabled)2008 void QgsRasterLayerProperties::toggleColorizeControls( bool colorizeEnabled )
2009 {
2010   // Enable or disable colorize controls based on checkbox
2011   btnColorizeColor->setEnabled( colorizeEnabled );
2012   sliderColorizeStrength->setEnabled( colorizeEnabled );
2013   spinColorizeStrength->setEnabled( colorizeEnabled );
2014 }
2015 
2016 
redGradient()2017 QLinearGradient QgsRasterLayerProperties::redGradient()
2018 {
2019   //define a gradient
2020   // TODO change this to actual polygon dims
2021   QLinearGradient myGradient = QLinearGradient( mGradientWidth, 0, mGradientWidth, mGradientHeight );
2022   myGradient.setColorAt( 0.0, QColor( 242, 14, 25, 190 ) );
2023   myGradient.setColorAt( 0.5, QColor( 175, 29, 37, 190 ) );
2024   myGradient.setColorAt( 1.0, QColor( 114, 17, 22, 190 ) );
2025   return myGradient;
2026 }
greenGradient()2027 QLinearGradient QgsRasterLayerProperties::greenGradient()
2028 {
2029   //define a gradient
2030   // TODO change this to actual polygon dims
2031   QLinearGradient myGradient = QLinearGradient( mGradientWidth, 0, mGradientWidth, mGradientHeight );
2032   myGradient.setColorAt( 0.0, QColor( 48, 168, 5, 190 ) );
2033   myGradient.setColorAt( 0.8, QColor( 36, 122, 4, 190 ) );
2034   myGradient.setColorAt( 1.0, QColor( 21, 71, 2, 190 ) );
2035   return myGradient;
2036 }
blueGradient()2037 QLinearGradient QgsRasterLayerProperties::blueGradient()
2038 {
2039   //define a gradient
2040   // TODO change this to actual polygon dims
2041   QLinearGradient myGradient = QLinearGradient( mGradientWidth, 0, mGradientWidth, mGradientHeight );
2042   myGradient.setColorAt( 0.0, QColor( 30, 0, 106, 190 ) );
2043   myGradient.setColorAt( 0.2, QColor( 30, 72, 128, 190 ) );
2044   myGradient.setColorAt( 1.0, QColor( 30, 223, 196, 190 ) );
2045   return myGradient;
2046 }
grayGradient()2047 QLinearGradient QgsRasterLayerProperties::grayGradient()
2048 {
2049   //define a gradient
2050   // TODO change this to actual polygon dims
2051   QLinearGradient myGradient = QLinearGradient( mGradientWidth, 0, mGradientWidth, mGradientHeight );
2052   myGradient.setColorAt( 0.0, QColor( 5, 5, 5, 190 ) );
2053   myGradient.setColorAt( 0.8, QColor( 122, 122, 122, 190 ) );
2054   myGradient.setColorAt( 1.0, QColor( 220, 220, 220, 190 ) );
2055   return myGradient;
2056 }
highlightGradient()2057 QLinearGradient QgsRasterLayerProperties::highlightGradient()
2058 {
2059   //define another gradient for the highlight
2060   // TODO change this to actual polygon dims
2061   QLinearGradient myGradient = QLinearGradient( mGradientWidth, 0, mGradientWidth, mGradientHeight );
2062   myGradient.setColorAt( 1.0, QColor( 255, 255, 255, 50 ) );
2063   myGradient.setColorAt( 0.5, QColor( 255, 255, 255, 100 ) );
2064   myGradient.setColorAt( 0.0, QColor( 255, 255, 255, 150 ) );
2065   return myGradient;
2066 }
2067 
2068 
2069 
2070 //
2071 //
2072 // Next four methods for saving and restoring qml style state
2073 //
2074 //
loadDefaultStyle_clicked()2075 void QgsRasterLayerProperties::loadDefaultStyle_clicked()
2076 {
2077   bool defaultLoadedFlag = false;
2078   QString myMessage = mRasterLayer->loadDefaultStyle( defaultLoadedFlag );
2079   //reset if the default style was loaded OK only
2080   if ( defaultLoadedFlag )
2081   {
2082     syncToLayer();
2083   }
2084   else
2085   {
2086     //otherwise let the user know what went wrong
2087     QMessageBox::information( this,
2088                               tr( "Default Style" ),
2089                               myMessage
2090                             );
2091   }
2092 }
2093 
saveDefaultStyle_clicked()2094 void QgsRasterLayerProperties::saveDefaultStyle_clicked()
2095 {
2096 
2097   apply(); // make sure the style to save is up-to-date
2098 
2099   // a flag passed by reference
2100   bool defaultSavedFlag = false;
2101   // after calling this the above flag will be set true for success
2102   // or false if the save operation failed
2103   QString myMessage = mRasterLayer->saveDefaultStyle( defaultSavedFlag );
2104   if ( !defaultSavedFlag )
2105   {
2106     //let the user know what went wrong
2107     QMessageBox::information( this,
2108                               tr( "Default Style" ),
2109                               myMessage
2110                             );
2111   }
2112 }
2113 
2114 
loadStyle_clicked()2115 void QgsRasterLayerProperties::loadStyle_clicked()
2116 {
2117   QgsSettings settings;
2118   QString lastUsedDir = settings.value( QStringLiteral( "style/lastStyleDir" ), QDir::homePath() ).toString();
2119 
2120   QString fileName = QFileDialog::getOpenFileName(
2121                        this,
2122                        tr( "Load layer properties from style file" ),
2123                        lastUsedDir,
2124                        tr( "QGIS Layer Style File" ) + " (*.qml)" );
2125   if ( fileName.isEmpty() )
2126     return;
2127 
2128   // ensure the user never omits the extension from the file name
2129   if ( !fileName.endsWith( QLatin1String( ".qml" ), Qt::CaseInsensitive ) )
2130     fileName += QLatin1String( ".qml" );
2131 
2132   mOldStyle = mRasterLayer->styleManager()->style( mRasterLayer->styleManager()->currentStyle() );
2133 
2134   bool defaultLoadedFlag = false;
2135   QString message = mRasterLayer->loadNamedStyle( fileName, defaultLoadedFlag );
2136   if ( defaultLoadedFlag )
2137   {
2138     settings.setValue( QStringLiteral( "style/lastStyleDir" ), QFileInfo( fileName ).absolutePath() );
2139     syncToLayer();
2140   }
2141   else
2142   {
2143     QMessageBox::information( this, tr( "Save Style" ), message );
2144   }
2145 }
2146 
2147 
saveStyleAs_clicked()2148 void QgsRasterLayerProperties::saveStyleAs_clicked()
2149 {
2150   QgsSettings settings;
2151   QString lastUsedDir = settings.value( QStringLiteral( "style/lastStyleDir" ), QDir::homePath() ).toString();
2152 
2153   QString selectedFilter;
2154   QString outputFileName = QFileDialog::getSaveFileName(
2155                              this,
2156                              tr( "Save layer properties as style file" ),
2157                              lastUsedDir,
2158                              tr( "QGIS Layer Style File" ) + " (*.qml)" + ";;" + tr( "Styled Layer Descriptor" ) + " (*.sld)",
2159                              &selectedFilter );
2160   if ( outputFileName.isEmpty() )
2161     return;
2162 
2163   StyleType type;
2164   // use selectedFilter to set style type
2165   if ( selectedFilter.contains( QStringLiteral( ".qml" ), Qt::CaseInsensitive ) )
2166   {
2167     outputFileName = QgsFileUtils::ensureFileNameHasExtension( outputFileName, QStringList() << QStringLiteral( "qml" ) );
2168     type = StyleType::QML;
2169   }
2170   else
2171   {
2172     outputFileName = QgsFileUtils::ensureFileNameHasExtension( outputFileName, QStringList() << QStringLiteral( "sld" ) );
2173     type = StyleType::SLD;
2174   }
2175 
2176   apply(); // make sure the style to save is up-to-date
2177 
2178   // then export style
2179   bool defaultLoadedFlag = false;
2180   QString message;
2181   switch ( type )
2182   {
2183     case QML:
2184     {
2185       message = mRasterLayer->saveNamedStyle( outputFileName, defaultLoadedFlag );
2186       break;
2187     }
2188     case SLD:
2189     {
2190       message = mRasterLayer->saveSldStyle( outputFileName, defaultLoadedFlag );
2191       break;
2192     }
2193   }
2194   if ( defaultLoadedFlag )
2195   {
2196     settings.setValue( QStringLiteral( "style/lastStyleDir" ), QFileInfo( outputFileName ).absolutePath() );
2197     sync();
2198   }
2199   else
2200     QMessageBox::information( this, tr( "Save Style" ), message );
2201 }
2202 
restoreWindowModality()2203 void QgsRasterLayerProperties::restoreWindowModality()
2204 {
2205   hide();
2206   setModal( true );
2207   show();
2208   raise();
2209   activateWindow();
2210 }
2211 
2212 //
2213 //
2214 // Next four methods for saving and restoring QMD metadata
2215 //
2216 //
2217 
loadMetadata()2218 void QgsRasterLayerProperties::loadMetadata()
2219 {
2220   QgsSettings myQSettings;  // where we keep last used filter in persistent state
2221   QString myLastUsedDir = myQSettings.value( QStringLiteral( "style/lastStyleDir" ), QDir::homePath() ).toString();
2222 
2223   QString myFileName = QFileDialog::getOpenFileName( this, tr( "Load layer metadata from metadata file" ), myLastUsedDir,
2224                        tr( "QGIS Layer Metadata File" ) + " (*.qmd)" );
2225   if ( myFileName.isNull() )
2226   {
2227     return;
2228   }
2229 
2230   QString myMessage;
2231   bool defaultLoadedFlag = false;
2232   myMessage = mRasterLayer->loadNamedMetadata( myFileName, defaultLoadedFlag );
2233 
2234   //reset if the default style was loaded OK only
2235   if ( defaultLoadedFlag )
2236   {
2237     mMetadataWidget->setMetadata( &mRasterLayer->metadata() );
2238   }
2239   else
2240   {
2241     //let the user know what went wrong
2242     QMessageBox::warning( this, tr( "Load Metadata" ), myMessage );
2243   }
2244 
2245   QFileInfo myFI( myFileName );
2246   QString myPath = myFI.path();
2247   myQSettings.setValue( QStringLiteral( "style/lastStyleDir" ), myPath );
2248 
2249   activateWindow(); // set focus back to properties dialog
2250 }
2251 
saveMetadataAs()2252 void QgsRasterLayerProperties::saveMetadataAs()
2253 {
2254   QgsSettings myQSettings;  // where we keep last used filter in persistent state
2255   QString myLastUsedDir = myQSettings.value( QStringLiteral( "style/lastStyleDir" ), QDir::homePath() ).toString();
2256 
2257   QString myOutputFileName = QFileDialog::getSaveFileName( this, tr( "Save Layer Metadata as QMD" ),
2258                              myLastUsedDir, tr( "QMD File" ) + " (*.qmd)" );
2259   if ( myOutputFileName.isNull() ) //dialog canceled
2260   {
2261     return;
2262   }
2263 
2264   mMetadataWidget->acceptMetadata();
2265 
2266   //ensure the user never omitted the extension from the file name
2267   if ( !myOutputFileName.endsWith( QgsMapLayer::extensionPropertyType( QgsMapLayer::Metadata ), Qt::CaseInsensitive ) )
2268   {
2269     myOutputFileName += QgsMapLayer::extensionPropertyType( QgsMapLayer::Metadata );
2270   }
2271 
2272   bool defaultLoadedFlag = false;
2273   QString message = mRasterLayer->saveNamedMetadata( myOutputFileName, defaultLoadedFlag );
2274   if ( defaultLoadedFlag )
2275     myQSettings.setValue( QStringLiteral( "style/lastStyleDir" ), QFileInfo( myOutputFileName ).absolutePath() );
2276   else
2277     QMessageBox::information( this, tr( "Save Metadata" ), message );
2278 }
2279 
saveDefaultMetadata()2280 void QgsRasterLayerProperties::saveDefaultMetadata()
2281 {
2282   mMetadataWidget->acceptMetadata();
2283 
2284   bool defaultSavedFlag = false;
2285   QString errorMsg = mRasterLayer->saveDefaultMetadata( defaultSavedFlag );
2286   if ( !defaultSavedFlag )
2287   {
2288     QMessageBox::warning( this, tr( "Default Metadata" ), errorMsg );
2289   }
2290 }
2291 
loadDefaultMetadata()2292 void QgsRasterLayerProperties::loadDefaultMetadata()
2293 {
2294   bool defaultLoadedFlag = false;
2295   QString myMessage = mRasterLayer->loadNamedMetadata( mRasterLayer->metadataUri(), defaultLoadedFlag );
2296   //reset if the default metadata was loaded OK only
2297   if ( defaultLoadedFlag )
2298   {
2299     mMetadataWidget->setMetadata( &mRasterLayer->metadata() );
2300   }
2301   else
2302   {
2303     QMessageBox::information( this, tr( "Default Metadata" ), myMessage );
2304   }
2305 }
2306 
2307 
toggleBuildPyramidsButton()2308 void QgsRasterLayerProperties::toggleBuildPyramidsButton()
2309 {
2310   if ( lbxPyramidResolutions->selectedItems().empty() )
2311   {
2312     buttonBuildPyramids->setEnabled( false );
2313   }
2314   else
2315   {
2316     buttonBuildPyramids->setEnabled( true );
2317   }
2318 }
2319 
mResetColorRenderingBtn_clicked()2320 void QgsRasterLayerProperties::mResetColorRenderingBtn_clicked()
2321 {
2322   mBlendModeComboBox->setBlendMode( QPainter::CompositionMode_SourceOver );
2323   mSliderBrightness->setValue( 0 );
2324   mSliderContrast->setValue( 0 );
2325   mGammaSpinBox->setValue( 1.0 );
2326   sliderSaturation->setValue( 0 );
2327   comboGrayscale->setCurrentIndex( ( int ) QgsHueSaturationFilter::GrayscaleOff );
2328   mColorizeCheck->setChecked( false );
2329   sliderColorizeStrength->setValue( 100 );
2330 }
2331 
rasterIsMultiBandColor()2332 bool QgsRasterLayerProperties::rasterIsMultiBandColor()
2333 {
2334   return mRasterLayer && nullptr != dynamic_cast<QgsMultiBandColorRenderer *>( mRasterLayer->renderer() );
2335 }
2336 
updateInformationContent()2337 void QgsRasterLayerProperties::updateInformationContent()
2338 {
2339   const QString myStyle = QgsApplication::reportStyleSheet( QgsApplication::StyleSheetType::WebBrowser );
2340   // Inject the stylesheet
2341   const QString html { mRasterLayer->htmlMetadata().replace( QLatin1String( "<head>" ), QStringLiteral( R"raw(<head><style type="text/css">%1</style>)raw" ) ).arg( myStyle ) };
2342   mMetadataViewer->setHtml( html );
2343   mMetadataFilled = true;
2344 }
2345 
onCancel()2346 void QgsRasterLayerProperties::onCancel()
2347 {
2348   if ( mOldStyle.xmlData() != mRasterLayer->styleManager()->style( mRasterLayer->styleManager()->currentStyle() ).xmlData() )
2349   {
2350     // need to reset style to previous - style applied directly to the layer (not in apply())
2351     QString myMessage;
2352     QDomDocument doc( QStringLiteral( "qgis" ) );
2353     int errorLine, errorColumn;
2354     doc.setContent( mOldStyle.xmlData(), false, &myMessage, &errorLine, &errorColumn );
2355     mRasterLayer->importNamedStyle( doc, myMessage );
2356     syncToLayer();
2357   }
2358 }
2359 
showHelp()2360 void QgsRasterLayerProperties::showHelp()
2361 {
2362   const QVariant helpPage = mOptionsStackedWidget->currentWidget()->property( "helpPage" );
2363 
2364   if ( helpPage.isValid() )
2365   {
2366     QgsHelp::openHelp( helpPage.toString() );
2367   }
2368   else
2369   {
2370     QgsHelp::openHelp( QStringLiteral( "working_with_raster/raster_properties.html" ) );
2371   }
2372 }
2373 
updateGammaSpinBox(int value)2374 void QgsRasterLayerProperties::updateGammaSpinBox( int value )
2375 {
2376   whileBlocking( mGammaSpinBox )->setValue( value / 100.0 );
2377 }
2378 
updateGammaSlider(double value)2379 void QgsRasterLayerProperties::updateGammaSlider( double value )
2380 {
2381   whileBlocking( mSliderGamma )->setValue( value * 100 );
2382 }
2383