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 "qgsmetadataurlitemdelegate.h"
34 #include "qgsmultibandcolorrenderer.h"
35 #include "qgsmultibandcolorrendererwidget.h"
36 #include "qgsnative.h"
37 #include "qgspalettedrendererwidget.h"
38 #include "qgsprovidersourcewidgetproviderregistry.h"
39 #include "qgsprovidersourcewidget.h"
40 #include "qgsproject.h"
41 #include "qgsrasterbandstats.h"
42 #include "qgsrastercontourrendererwidget.h"
43 #include "qgsrasterdataprovider.h"
44 #include "qgsrasterhistogramwidget.h"
45 #include "qgsrastertransparencywidget.h"
46 #include "qgsrasteridentifyresult.h"
47 #include "qgsrasterlayer.h"
48 #include "qgsrasterlayerproperties.h"
49 #include "qgsrasterpyramid.h"
50 #include "qgsrasterrange.h"
51 #include "qgsrasterrenderer.h"
52 #include "qgsrasterrendererregistry.h"
53 #include "qgsrastertransparency.h"
54 #include "qgssinglebandgrayrendererwidget.h"
55 #include "qgssinglebandpseudocolorrendererwidget.h"
56 #include "qgshuesaturationfilter.h"
57 #include "qgshillshaderendererwidget.h"
58 #include "qgssettings.h"
59 #include "qgsdatumtransformdialog.h"
60 #include "qgsmaplayerlegend.h"
61 #include "qgsfileutils.h"
62 #include "qgswebview.h"
63 #include "qgsvectorlayer.h"
64 #include "qgsprovidermetadata.h"
65 #include "qgsproviderregistry.h"
66 #include "qgsrasterlayertemporalproperties.h"
67 #include "qgsdoublevalidator.h"
68 #include "qgsmaplayerconfigwidgetfactory.h"
69 
70 #include "qgsrasterlayertemporalpropertieswidget.h"
71 #include "qgsprojecttimesettings.h"
72 #include "qgsexpressioncontextutils.h"
73 
74 #include <QDesktopServices>
75 #include <QTableWidgetItem>
76 #include <QHeaderView>
77 #include <QTextStream>
78 #include <QFile>
79 #include <QFileDialog>
80 #include <QMessageBox>
81 #include <QPainter>
82 #include <QLinearGradient>
83 #include <QPainterPath>
84 #include <QPolygonF>
85 #include <QColorDialog>
86 #include <QList>
87 #include <QMouseEvent>
88 #include <QVector>
89 #include <QUrl>
90 #include <QMenu>
91 #include <QScreen>
92 #include <QRegularExpressionValidator>
93 #include <QRegularExpression>
94 
QgsRasterLayerProperties(QgsMapLayer * lyr,QgsMapCanvas * canvas,QWidget * parent,Qt::WindowFlags fl)95 QgsRasterLayerProperties::QgsRasterLayerProperties( QgsMapLayer *lyr, QgsMapCanvas *canvas, QWidget *parent, Qt::WindowFlags fl )
96   : QgsOptionsDialogBase( QStringLiteral( "RasterLayerProperties" ), parent, fl )
97     // Constant that signals property not used.
98   , TRSTRING_NOT_SET( tr( "Not Set" ) )
99   , mDefaultStandardDeviation( 0 )
100   , mDefaultRedBand( 0 )
101   , mDefaultGreenBand( 0 )
102   , mDefaultBlueBand( 0 )
103   , mRasterLayer( qobject_cast<QgsRasterLayer *>( lyr ) )
104   , mGradientHeight( 0.0 )
105   , mGradientWidth( 0.0 )
106   , mMapCanvas( canvas )
107   , mMetadataFilled( false )
108 {
109   mGrayMinimumMaximumEstimated = true;
110   mRGBMinimumMaximumEstimated = true;
111 
112   setupUi( this );
113 
114   mMetadataViewer = new QgsWebView( this );
115   mOptsPage_Information->layout()->addWidget( mMetadataViewer );
116 
117   mRasterTransparencyWidget = new QgsRasterTransparencyWidget( mRasterLayer, canvas, this );
118 
119   transparencyScrollArea->setWidget( mRasterTransparencyWidget );
120 
121   connect( buttonBuildPyramids, &QPushButton::clicked, this, &QgsRasterLayerProperties::buttonBuildPyramids_clicked );
122   connect( mCrsSelector, &QgsProjectionSelectionWidget::crsChanged, this, &QgsRasterLayerProperties::mCrsSelector_crsChanged );
123   connect( mRenderTypeComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsRasterLayerProperties::mRenderTypeComboBox_currentIndexChanged );
124   connect( mResetColorRenderingBtn, &QToolButton::clicked, this, &QgsRasterLayerProperties::mResetColorRenderingBtn_clicked );
125   connect( buttonRemoveMetadataUrl, &QPushButton::clicked, this, &QgsRasterLayerProperties::removeSelectedMetadataUrl );
126   connect( buttonAddMetadataUrl, &QPushButton::clicked, this, &QgsRasterLayerProperties::addMetadataUrl );
127   // QgsOptionsDialogBase handles saving/restoring of geometry, splitter and current tab states,
128   // switching vertical tabs between icon/text to icon-only modes (splitter collapsed to left),
129   // and connecting QDialogButtonBox's accepted/rejected signals to dialog's accept/reject slots
130   initOptionsBase( false );
131   connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsRasterLayerProperties::showHelp );
132 
133   mSourceGroupBox->hide();
134 
135   mBtnStyle = new QPushButton( tr( "Style" ) );
136   QMenu *menuStyle = new QMenu( this );
137   menuStyle->addAction( tr( "Load Style…" ), this, &QgsRasterLayerProperties::loadStyle_clicked );
138   menuStyle->addAction( tr( "Save Style…" ), this, &QgsRasterLayerProperties::saveStyleAs_clicked );
139   menuStyle->addSeparator();
140   menuStyle->addAction( tr( "Save as Default" ), this, &QgsRasterLayerProperties::saveDefaultStyle_clicked );
141   menuStyle->addAction( tr( "Restore Default" ), this, &QgsRasterLayerProperties::loadDefaultStyle_clicked );
142   mBtnStyle->setMenu( menuStyle );
143   connect( menuStyle, &QMenu::aboutToShow, this, &QgsRasterLayerProperties::aboutToShowStyleMenu );
144   buttonBox->addButton( mBtnStyle, QDialogButtonBox::ResetRole );
145 
146   mBtnMetadata = new QPushButton( tr( "Metadata" ), this );
147   QMenu *menuMetadata = new QMenu( this );
148   mActionLoadMetadata = menuMetadata->addAction( tr( "Load Metadata…" ), this, &QgsRasterLayerProperties::loadMetadata );
149   mActionSaveMetadataAs = menuMetadata->addAction( tr( "Save Metadata…" ), this, &QgsRasterLayerProperties::saveMetadataAs );
150   menuMetadata->addSeparator();
151   menuMetadata->addAction( tr( "Save as Default" ), this, &QgsRasterLayerProperties::saveDefaultMetadata );
152   menuMetadata->addAction( tr( "Restore Default" ), this, &QgsRasterLayerProperties::loadDefaultMetadata );
153   mBtnMetadata->setMenu( menuMetadata );
154   buttonBox->addButton( mBtnMetadata, QDialogButtonBox::ResetRole );
155 
156   connect( lyr->styleManager(), &QgsMapLayerStyleManager::currentStyleChanged, this, &QgsRasterLayerProperties::syncToLayer );
157 
158   connect( this, &QDialog::accepted, this, &QgsRasterLayerProperties::apply );
159   connect( this, &QDialog::rejected, this, &QgsRasterLayerProperties::onCancel );
160 
161   connect( buttonBox->button( QDialogButtonBox::Apply ), &QAbstractButton::clicked, this, &QgsRasterLayerProperties::apply );
162 
163   // brightness/contrast controls
164   connect( mSliderBrightness, &QAbstractSlider::valueChanged, mBrightnessSpinBox, &QSpinBox::setValue );
165   connect( mBrightnessSpinBox, static_cast < void ( QSpinBox::* )( int ) > ( &QSpinBox::valueChanged ), mSliderBrightness, &QAbstractSlider::setValue );
166   mBrightnessSpinBox->setClearValue( 0 );
167 
168   connect( mSliderContrast, &QAbstractSlider::valueChanged, mContrastSpinBox, &QSpinBox::setValue );
169   connect( mContrastSpinBox, static_cast < void ( QSpinBox::* )( int ) > ( &QSpinBox::valueChanged ), mSliderContrast, &QAbstractSlider::setValue );
170   mContrastSpinBox->setClearValue( 0 );
171 
172   // gamma correction controls
173   connect( mSliderGamma, &QAbstractSlider::valueChanged, this, &QgsRasterLayerProperties::updateGammaSpinBox );
174   connect( mGammaSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsRasterLayerProperties::updateGammaSlider );
175   mGammaSpinBox->setClearValue( 1.0 );
176 
177   // Connect saturation slider and spin box
178   connect( sliderSaturation, &QAbstractSlider::valueChanged, spinBoxSaturation, &QSpinBox::setValue );
179   connect( spinBoxSaturation, static_cast < void ( QSpinBox::* )( int ) > ( &QSpinBox::valueChanged ), sliderSaturation, &QAbstractSlider::setValue );
180   spinBoxSaturation->setClearValue( 0 );
181 
182   // Connect colorize strength slider and spin box
183   connect( sliderColorizeStrength, &QAbstractSlider::valueChanged, spinColorizeStrength, &QSpinBox::setValue );
184   connect( spinColorizeStrength, static_cast < void ( QSpinBox::* )( int ) > ( &QSpinBox::valueChanged ), sliderColorizeStrength, &QAbstractSlider::setValue );
185   spinColorizeStrength->setClearValue( 100 );
186 
187   // enable or disable saturation slider and spin box depending on grayscale combo choice
188   connect( comboGrayscale, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsRasterLayerProperties::toggleSaturationControls );
189 
190   // enable or disable colorize colorbutton with colorize checkbox
191   connect( mColorizeCheck, &QAbstractButton::toggled, this, &QgsRasterLayerProperties::toggleColorizeControls );
192 
193   // enable or disable Build Pyramids button depending on selection in pyramid list
194   connect( lbxPyramidResolutions, &QListWidget::itemSelectionChanged, this, &QgsRasterLayerProperties::toggleBuildPyramidsButton );
195 
196   connect( mRefreshLayerCheckBox, &QCheckBox::toggled, mRefreshLayerIntervalSpinBox, &QDoubleSpinBox::setEnabled );
197 
198   // set up the scale based layer visibility stuff....
199   mScaleRangeWidget->setMapCanvas( mMapCanvas );
200   chkUseScaleDependentRendering->setChecked( lyr->hasScaleBasedVisibility() );
201   mScaleRangeWidget->setScaleRange( lyr->minimumScale(), lyr->maximumScale() );
202 
203   // Setup the layer metadata URL
204   tableViewMetadataUrl->setSelectionMode( QAbstractItemView::SingleSelection );
205   tableViewMetadataUrl->setSelectionBehavior( QAbstractItemView::SelectRows );
206   tableViewMetadataUrl->horizontalHeader()->setStretchLastSection( true );
207   tableViewMetadataUrl->horizontalHeader()->setSectionResizeMode( QHeaderView::Stretch );
208 
209   mMetadataUrlModel = new QStandardItemModel( tableViewMetadataUrl );
210   mMetadataUrlModel->clear();
211   mMetadataUrlModel->setColumnCount( 3 );
212   QStringList metadataUrlHeaders;
213   metadataUrlHeaders << tr( "URL" ) << tr( "Type" ) << tr( "Format" );
214   mMetadataUrlModel->setHorizontalHeaderLabels( metadataUrlHeaders );
215   tableViewMetadataUrl->setModel( mMetadataUrlModel );
216   tableViewMetadataUrl->setItemDelegate( new MetadataUrlItemDelegate( this ) );
217 
218   // build GUI components
219   QIcon myPyramidPixmap( QgsApplication::getThemeIcon( "/mIconPyramid.svg" ) );
220   QIcon myNoPyramidPixmap( QgsApplication::getThemeIcon( "/mIconNoPyramid.svg" ) );
221 
222   mRasterTransparencyWidget->pbnAddValuesManually->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/symbologyAdd.svg" ) ) );
223   mRasterTransparencyWidget->pbnAddValuesFromDisplay->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionContextHelp.png" ) ) );
224   mRasterTransparencyWidget->pbnRemoveSelectedRow->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/symbologyRemove.svg" ) ) );
225   mRasterTransparencyWidget->pbnDefaultValues->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionOpenTable.svg" ) ) );
226   mRasterTransparencyWidget->pbnImportTransparentPixelValues->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionFileOpen.svg" ) ) );
227   mRasterTransparencyWidget->pbnExportTransparentPixelValues->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionFileSave.svg" ) ) );
228 
229 
230   if ( !mRasterLayer )
231   {
232     return;
233   }
234 
235   mBackupCrs = mRasterLayer->crs();
236 
237   // Handles window modality raising canvas
238   if ( mMapCanvas && mRasterTransparencyWidget->pixelSelectorTool() )
239   {
240 
241     connect( mRasterTransparencyWidget->pixelSelectorTool(), &QgsMapToolEmitPoint::deactivated, this, [ = ]
242     {
243       hide();
244       setModal( true );
245       show();
246       raise();
247       activateWindow();
248     } );
249 
250     connect( mRasterTransparencyWidget->pbnAddValuesFromDisplay, &QPushButton::clicked, this, [ = ]
251     {
252       hide();
253       setModal( false );
254 
255       // Transfer focus to the canvas to use the selector tool
256       mMapCanvas->window()->raise();
257       mMapCanvas->window()->activateWindow();
258       mMapCanvas->window()->setFocus();
259     } );
260   }
261 
262   mContext << QgsExpressionContextUtils::globalScope()
263            << QgsExpressionContextUtils::projectScope( QgsProject::instance() )
264            << QgsExpressionContextUtils::atlasScope( nullptr );
265   if ( mMapCanvas )
266     mContext << QgsExpressionContextUtils::mapSettingsScope( mMapCanvas->mapSettings() );
267   mContext << QgsExpressionContextUtils::layerScope( mRasterLayer );
268 
269   QgsRasterDataProvider *provider = mRasterLayer->dataProvider();
270 
271   // Only do pyramids if dealing directly with GDAL.
272   if ( provider && provider->capabilities() & QgsRasterDataProvider::BuildPyramids )
273   {
274     // initialize resampling methods
275     cboResamplingMethod->clear();
276 
277     const auto constProviderType = QgsRasterDataProvider::pyramidResamplingMethods( mRasterLayer->providerType() );
278     for ( const QPair<QString, QString> &method : std::as_const( constProviderType ) )
279     {
280       cboResamplingMethod->addItem( method.second, method.first );
281     }
282 
283     // keep it in sync with qgsrasterpyramidsoptionwidget.cpp
284     QString prefix = provider->name() + "/driverOptions/_pyramids/";
285     QgsSettings mySettings;
286     QString defaultMethod = mySettings.value( prefix + "resampling", "AVERAGE" ).toString();
287     int idx = cboResamplingMethod->findData( defaultMethod );
288     if ( idx >= 0 )
289       cboResamplingMethod->setCurrentIndex( idx );
290 
291 
292     // build pyramid list
293     const QList< QgsRasterPyramid > myPyramidList = provider->buildPyramidList();
294 
295     for ( const QgsRasterPyramid &pyramid : myPyramidList )
296     {
297       if ( pyramid.getExists() )
298       {
299         lbxPyramidResolutions->addItem( new QListWidgetItem( myPyramidPixmap,
300                                         QString::number( pyramid.getXDim() ) + QStringLiteral( " x " ) +
301                                         QString::number( pyramid.getYDim() ) ) );
302       }
303       else
304       {
305         lbxPyramidResolutions->addItem( new QListWidgetItem( myNoPyramidPixmap,
306                                         QString::number( pyramid.getXDim() ) + QStringLiteral( " x " ) +
307                                         QString::number( pyramid.getYDim() ) ) );
308       }
309     }
310   }
311   else
312   {
313     // disable Pyramids tab completely
314     mOptsPage_Pyramids->setEnabled( false );
315   }
316 
317   // We can calculate histogram for all data sources but estimated only if
318   // size is unknown - could also be enabled if well supported (estimated histogram
319   // and let user know that it is estimated)
320   if ( !provider || !( provider->capabilities() & QgsRasterDataProvider::Size ) )
321   {
322     // disable Histogram tab completely
323     mOptsPage_Histogram->setEnabled( false );
324   }
325 
326   QVBoxLayout *layout = new QVBoxLayout( metadataFrame );
327   layout->setContentsMargins( 0, 0, 0, 0 );
328   mMetadataWidget = new QgsMetadataWidget( this, mRasterLayer );
329   mMetadataWidget->layout()->setContentsMargins( 0, 0, 0, 0 );
330   mMetadataWidget->setMapCanvas( mMapCanvas );
331   layout->addWidget( mMetadataWidget );
332   metadataFrame->setLayout( layout );
333 
334   QVBoxLayout *temporalLayout = new QVBoxLayout( temporalFrame );
335   temporalLayout->setContentsMargins( 0, 0, 0, 0 );
336   mTemporalWidget = new QgsRasterLayerTemporalPropertiesWidget( this, mRasterLayer );
337   temporalLayout->addWidget( mTemporalWidget );
338 
339   QgsDebugMsgLevel( "Setting crs to " + mRasterLayer->crs().toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED ), 2 );
340   QgsDebugMsgLevel( "Setting crs to " + mRasterLayer->crs().userFriendlyIdentifier(), 2 );
341   mCrsSelector->setCrs( mRasterLayer->crs() );
342 
343   // Set text for pyramid info box
344   QString pyramidFormat( QStringLiteral( "<h2>%1</h2><p>%2 %3 %4</p><b><font color='red'><p>%5</p><p>%6</p>" ) );
345   QString pyramidHeader    = tr( "Description" );
346   QString pyramidSentence1 = tr( "Large resolution raster layers can slow navigation in QGIS." );
347   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." );
348   QString pyramidSentence3 = tr( "You must have write access in the directory where the original data is stored to build pyramids." );
349   QString pyramidSentence4 = tr( "Please note that building internal pyramids may alter the original data file and once created they cannot be removed!" );
350   QString pyramidSentence5 = tr( "Please note that building internal pyramids could corrupt your image - always make a backup of your data first!" );
351 
352   tePyramidDescription->setHtml( pyramidFormat.arg( pyramidHeader,
353                                  pyramidSentence1,
354                                  pyramidSentence2,
355                                  pyramidSentence3,
356                                  pyramidSentence4,
357                                  pyramidSentence5 ) );
358 
359   //resampling
360   mResamplingGroupBox->setSaveCheckedState( true );
361   mResamplingUtils.initWidgets( mRasterLayer, mZoomedInResamplingComboBox, mZoomedOutResamplingComboBox, mMaximumOversamplingSpinBox, mCbEarlyResampling );
362   mResamplingUtils.refreshWidgetsFromLayer();
363 
364   const QgsRasterRenderer *renderer = mRasterLayer->renderer();
365 
366   btnColorizeColor->setColorDialogTitle( tr( "Select Color" ) );
367   btnColorizeColor->setContext( QStringLiteral( "symbology" ) );
368 
369   // Hue and saturation color control
370   const QgsHueSaturationFilter *hueSaturationFilter = mRasterLayer->hueSaturationFilter();
371   //set hue and saturation controls to current values
372   if ( hueSaturationFilter )
373   {
374     sliderSaturation->setValue( hueSaturationFilter->saturation() );
375     comboGrayscale->setCurrentIndex( ( int ) hueSaturationFilter->grayscaleMode() );
376 
377     // Set initial state of saturation controls based on grayscale mode choice
378     toggleSaturationControls( static_cast<int>( hueSaturationFilter->grayscaleMode() ) );
379 
380     // Set initial state of colorize controls
381     mColorizeCheck->setChecked( hueSaturationFilter->colorizeOn() );
382     btnColorizeColor->setColor( hueSaturationFilter->colorizeColor() );
383     toggleColorizeControls( hueSaturationFilter->colorizeOn() );
384     sliderColorizeStrength->setValue( hueSaturationFilter->colorizeStrength() );
385     mInvertColorsCheck->setChecked( hueSaturationFilter->invertColors() );
386   }
387 
388   //blend mode
389   mBlendModeComboBox->setBlendMode( mRasterLayer->blendMode() );
390 
391   //transparency band
392   if ( provider )
393   {
394     mRasterTransparencyWidget->cboxTransparencyBand->setShowNotSetOption( true, tr( "None" ) );
395     mRasterTransparencyWidget->cboxTransparencyBand->setLayer( mRasterLayer );
396 
397 // Alpha band is set in sync()
398 #if 0
399     if ( renderer )
400     {
401       QgsDebugMsg( QStringLiteral( "alphaBand = %1" ).arg( renderer->alphaBand() ) );
402       if ( renderer->alphaBand() > 0 )
403       {
404         cboxTransparencyBand->setCurrentIndex( cboxTransparencyBand->findData( renderer->alphaBand() ) );
405       }
406     }
407 #endif
408   }
409 
410   // create histogram widget
411   mHistogramWidget = nullptr;
412   if ( mOptsPage_Histogram->isEnabled() )
413   {
414     mHistogramWidget = new QgsRasterHistogramWidget( mRasterLayer, mOptsPage_Histogram );
415     mHistogramStackedWidget->addWidget( mHistogramWidget );
416   }
417 
418   //insert renderer widgets into registry
419   QgsApplication::rasterRendererRegistry()->insertWidgetFunction( QStringLiteral( "paletted" ), QgsPalettedRendererWidget::create );
420   QgsApplication::rasterRendererRegistry()->insertWidgetFunction( QStringLiteral( "multibandcolor" ), QgsMultiBandColorRendererWidget::create );
421   QgsApplication::rasterRendererRegistry()->insertWidgetFunction( QStringLiteral( "singlebandpseudocolor" ), QgsSingleBandPseudoColorRendererWidget::create );
422   QgsApplication::rasterRendererRegistry()->insertWidgetFunction( QStringLiteral( "singlebandgray" ), QgsSingleBandGrayRendererWidget::create );
423   QgsApplication::rasterRendererRegistry()->insertWidgetFunction( QStringLiteral( "hillshade" ), QgsHillshadeRendererWidget::create );
424   QgsApplication::rasterRendererRegistry()->insertWidgetFunction( QStringLiteral( "contour" ), QgsRasterContourRendererWidget::create );
425 
426   //fill available renderers into combo box
427   QgsRasterRendererRegistryEntry entry;
428   mDisableRenderTypeComboBoxCurrentIndexChanged = true;
429   const auto constRenderersList = QgsApplication::rasterRendererRegistry()->renderersList();
430   for ( const QString &name : constRenderersList )
431   {
432     if ( QgsApplication::rasterRendererRegistry()->rendererData( name, entry ) )
433     {
434       if ( ( mRasterLayer->rasterType() != QgsRasterLayer::ColorLayer && entry.name != QLatin1String( "singlebandcolordata" ) ) ||
435            ( mRasterLayer->rasterType() == QgsRasterLayer::ColorLayer && entry.name == QLatin1String( "singlebandcolordata" ) ) )
436       {
437         mRenderTypeComboBox->addItem( entry.visibleName, entry.name );
438       }
439     }
440   }
441   mDisableRenderTypeComboBoxCurrentIndexChanged = false;
442 
443   int widgetIndex = 0;
444   if ( renderer )
445   {
446     QString rendererType = renderer->type();
447     widgetIndex = mRenderTypeComboBox->findData( rendererType );
448     if ( widgetIndex != -1 )
449     {
450       mDisableRenderTypeComboBoxCurrentIndexChanged = true;
451       mRenderTypeComboBox->setCurrentIndex( widgetIndex );
452       mDisableRenderTypeComboBoxCurrentIndexChanged = false;
453     }
454 
455     if ( rendererType == QLatin1String( "singlebandcolordata" ) && mRenderTypeComboBox->count() == 1 )
456     {
457       // no band rendering options for singlebandcolordata, so minimize group box
458       QSizePolicy sizep = mBandRenderingGrpBx->sizePolicy();
459       sizep.setVerticalStretch( 0 );
460       sizep.setVerticalPolicy( QSizePolicy::Maximum );
461       mBandRenderingGrpBx->setSizePolicy( sizep );
462       mBandRenderingGrpBx->updateGeometry();
463     }
464 
465     if ( mRasterLayer->providerType() != QLatin1String( "wms" ) )
466     {
467       mWMSPrintGroupBox->hide();
468       mPublishDataSourceUrlCheckBox->hide();
469       mBackgroundLayerCheckBox->hide();
470     }
471   }
472 
473 #ifdef WITH_QTWEBKIT
474   // Setup information tab
475 
476   const int horizontalDpi = logicalDpiX();
477 
478   // Adjust zoom: text is ok, but HTML seems rather big at least on Linux/KDE
479   if ( horizontalDpi > 96 )
480   {
481     mMetadataViewer->setZoomFactor( mMetadataViewer->zoomFactor() * 0.9 );
482   }
483   mMetadataViewer->page()->setLinkDelegationPolicy( QWebPage::LinkDelegationPolicy::DelegateAllLinks );
484   connect( mMetadataViewer->page(), &QWebPage::linkClicked, this, &QgsRasterLayerProperties::urlClicked );
485   mMetadataViewer->page()->settings()->setAttribute( QWebSettings::DeveloperExtrasEnabled, true );
486   mMetadataViewer->page()->settings()->setAttribute( QWebSettings::JavascriptEnabled, true );
487 
488 #endif
489 
490   initializeDataDefinedButton( mRasterTransparencyWidget->mOpacityDDBtn, QgsRasterPipe::RendererOpacity );
491 
492   mRenderTypeComboBox_currentIndexChanged( widgetIndex );
493 
494   // update based on lyr's current state
495   sync();
496 
497   QgsSettings settings;
498   // if dialog hasn't been opened/closed yet, default to Styles tab, which is used most often
499   // this will be read by restoreOptionsBaseUi()
500   if ( !settings.contains( QStringLiteral( "/Windows/RasterLayerProperties/tab" ) ) )
501   {
502     settings.setValue( QStringLiteral( "Windows/RasterLayerProperties/tab" ),
503                        mOptStackedWidget->indexOf( mOptsPage_Style ) );
504   }
505 
506   mResetColorRenderingBtn->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionUndo.svg" ) ) );
507 
508   QString title = tr( "Layer Properties — %1" ).arg( lyr->name() );
509 
510   if ( !mRasterLayer->styleManager()->isDefault( mRasterLayer->styleManager()->currentStyle() ) )
511     title += QStringLiteral( " (%1)" ).arg( mRasterLayer->styleManager()->currentStyle() );
512   restoreOptionsBaseUi( title );
513   optionsStackedWidget_CurrentChanged( mOptionsStackedWidget->currentIndex() );
514 
515   //Add help page references
516   mOptsPage_Information->setProperty( "helpPage", QStringLiteral( "working_with_raster/raster_properties.html#information-properties" ) );
517   mOptsPage_Source->setProperty( "helpPage", QStringLiteral( "working_with_raster/raster_properties.html#source-properties" ) );
518   mOptsPage_Style->setProperty( "helpPage", QStringLiteral( "working_with_raster/raster_properties.html#symbology-properties" ) );
519   mOptsPage_Transparency->setProperty( "helpPage", QStringLiteral( "working_with_raster/raster_properties.html#transparency-properties" ) );
520 
521   if ( mOptsPage_Histogram )
522     mOptsPage_Histogram->setProperty( "helpPage", QStringLiteral( "working_with_raster/raster_properties.html#histogram-properties" ) );
523 
524   mOptsPage_Rendering->setProperty( "helpPage", QStringLiteral( "working_with_raster/raster_properties.html#rendering-properties" ) );
525 
526   if ( mOptsPage_Pyramids )
527     mOptsPage_Pyramids->setProperty( "helpPage", QStringLiteral( "working_with_raster/raster_properties.html#pyramids-properties" ) );
528 
529   mOptsPage_Metadata->setProperty( "helpPage", QStringLiteral( "working_with_raster/raster_properties.html#metadata-properties" ) );
530   mOptsPage_Legend->setProperty( "helpPage", QStringLiteral( "working_with_raster/raster_properties.html#legend-properties" ) );
531   mOptsPage_Server->setProperty( "helpPage", QStringLiteral( "working_with_raster/raster_properties.html#server-properties" ) );
532 }
533 
addPropertiesPageFactory(const QgsMapLayerConfigWidgetFactory * factory)534 void QgsRasterLayerProperties::addPropertiesPageFactory( const QgsMapLayerConfigWidgetFactory *factory )
535 {
536   if ( !factory->supportsLayer( mRasterLayer ) || !factory->supportLayerPropertiesDialog() )
537   {
538     return;
539   }
540 
541   QgsMapLayerConfigWidget *page = factory->createWidget( mRasterLayer, nullptr, false, this );
542   switch ( factory->parentPage() )
543   {
544     case QgsMapLayerConfigWidgetFactory::ParentPage::NoParent:
545     {
546       mLayerPropertiesPages << page;
547 
548       const QString beforePage = factory->layerPropertiesPagePositionHint();
549       if ( beforePage.isEmpty() )
550         addPage( factory->title(), factory->title(), factory->icon(), page );
551       else
552         insertPage( factory->title(), factory->title(), factory->icon(), page, beforePage );
553       break;
554     }
555 
556     case QgsMapLayerConfigWidgetFactory::ParentPage::Temporal:
557       mTemporalWidget->addWidget( page );
558       break;
559   }
560 }
561 
createExpressionContext() const562 QgsExpressionContext QgsRasterLayerProperties::createExpressionContext() const
563 {
564   return mContext;
565 }
566 
setRendererWidget(const QString & rendererName)567 void QgsRasterLayerProperties::setRendererWidget( const QString &rendererName )
568 {
569   QgsDebugMsgLevel( "rendererName = " + rendererName, 3 );
570   QgsRasterRendererWidget *oldWidget = mRendererWidget;
571   QgsRasterRenderer *oldRenderer = mRasterLayer->renderer();
572 
573   int alphaBand = -1;
574   double opacity = 1;
575   QColor nodataColor;
576   if ( oldRenderer )
577   {
578     // Retain alpha band and opacity when switching renderer
579     alphaBand = oldRenderer->alphaBand();
580     opacity = oldRenderer->opacity();
581     nodataColor = oldRenderer->nodataColor();
582   }
583 
584   QgsRasterRendererRegistryEntry rendererEntry;
585   if ( QgsApplication::rasterRendererRegistry()->rendererData( rendererName, rendererEntry ) )
586   {
587     if ( rendererEntry.widgetCreateFunction ) //single band color data renderer e.g. has no widget
588     {
589       QgsDebugMsgLevel( QStringLiteral( "renderer has widgetCreateFunction" ), 3 );
590       // Current canvas extent (used to calc min/max) in layer CRS
591       QgsRectangle myExtent = mMapCanvas->mapSettings().outputExtentToLayerExtent( mRasterLayer, mMapCanvas->extent() );
592       if ( oldWidget && ( !oldRenderer || rendererName != oldRenderer->type() ) )
593       {
594         if ( rendererName == QLatin1String( "singlebandgray" ) )
595         {
596           whileBlocking( mRasterLayer )->setRenderer( QgsApplication::rasterRendererRegistry()->defaultRendererForDrawingStyle( QgsRaster::SingleBandGray, mRasterLayer->dataProvider() ) );
597           whileBlocking( mRasterLayer )->setDefaultContrastEnhancement();
598         }
599         else if ( rendererName == QLatin1String( "multibandcolor" ) )
600         {
601           whileBlocking( mRasterLayer )->setRenderer( QgsApplication::rasterRendererRegistry()->defaultRendererForDrawingStyle( QgsRaster::MultiBandColor, mRasterLayer->dataProvider() ) );
602           whileBlocking( mRasterLayer )->setDefaultContrastEnhancement();
603         }
604       }
605       mRasterLayer->renderer()->setAlphaBand( alphaBand );
606       mRasterLayer->renderer()->setOpacity( opacity );
607       mRasterLayer->renderer()->setNodataColor( nodataColor );
608       mRendererWidget = rendererEntry.widgetCreateFunction( mRasterLayer, myExtent );
609       mRendererWidget->setMapCanvas( mMapCanvas );
610       mRendererStackedWidget->addWidget( mRendererWidget );
611       if ( oldWidget )
612       {
613         //compare used bands in new and old renderer and reset transparency dialog if different
614         std::unique_ptr<QgsRasterRenderer> oldRenderer;
615         oldRenderer.reset( oldWidget->renderer() );
616         std::unique_ptr<QgsRasterRenderer> newRenderer;
617         newRenderer.reset( mRendererWidget->renderer() );
618         const QList<int> oldBands = oldRenderer->usesBands();
619         const QList<int> newBands = newRenderer->usesBands();
620         if ( oldBands != newBands )
621         {
622           mRasterTransparencyWidget->syncToLayer();
623         }
624       }
625     }
626   }
627 
628   const int widgetIndex = mRenderTypeComboBox->findData( rendererName );
629   if ( widgetIndex != -1 )
630   {
631     mDisableRenderTypeComboBoxCurrentIndexChanged = true;
632     mRenderTypeComboBox->setCurrentIndex( widgetIndex );
633     mDisableRenderTypeComboBoxCurrentIndexChanged = false;
634   }
635 
636   if ( mRendererWidget != oldWidget )
637     delete oldWidget;
638 
639   if ( mHistogramWidget )
640   {
641     mHistogramWidget->setRendererWidget( rendererName, mRendererWidget );
642   }
643 }
644 
sync()645 void QgsRasterLayerProperties::sync()
646 {
647   QgsSettings myQSettings;
648 
649   if ( !mSourceWidget )
650   {
651     mSourceWidget = QgsGui::sourceWidgetProviderRegistry()->createWidget( mRasterLayer );
652     if ( mSourceWidget )
653     {
654       QHBoxLayout *layout = new QHBoxLayout();
655       layout->addWidget( mSourceWidget );
656       mSourceGroupBox->setLayout( layout );
657       mSourceGroupBox->show();
658 
659       connect( mSourceWidget, &QgsProviderSourceWidget::validChanged, this, [ = ]( bool isValid )
660       {
661         buttonBox->button( QDialogButtonBox::Apply )->setEnabled( isValid );
662         buttonBox->button( QDialogButtonBox::Ok )->setEnabled( isValid );
663       } );
664     }
665   }
666 
667   if ( mSourceWidget )
668     mSourceWidget->setSourceUri( mRasterLayer->source() );
669 
670   const QgsRasterDataProvider *provider = mRasterLayer->dataProvider();
671   if ( !provider )
672     return;
673 
674   if ( provider->dataType( 1 ) == Qgis::DataType::ARGB32
675        || provider->dataType( 1 ) == Qgis::DataType::ARGB32_Premultiplied )
676   {
677     mRasterTransparencyWidget->gboxNoDataValue->setEnabled( false );
678     mRasterTransparencyWidget->gboxCustomTransparency->setEnabled( false );
679     mOptionsStackedWidget->setCurrentWidget( mOptsPage_Server );
680   }
681 
682   // TODO: Wouldn't it be better to just removeWidget() the tabs than delete them? [LS]
683   if ( !( provider->capabilities() & QgsRasterDataProvider::BuildPyramids ) )
684   {
685     if ( mOptsPage_Pyramids )
686     {
687       delete mOptsPage_Pyramids;
688       mOptsPage_Pyramids = nullptr;
689     }
690   }
691 
692   if ( !( provider->capabilities() & QgsRasterDataProvider::Size ) )
693   {
694     if ( mOptsPage_Histogram )
695     {
696       delete mOptsPage_Histogram;
697       mOptsPage_Histogram = nullptr;
698       delete mHistogramWidget;
699       mHistogramWidget = nullptr;
700     }
701   }
702 
703   QgsDebugMsgLevel( QStringLiteral( "populate transparency tab" ), 3 );
704 
705   /*
706    * Style tab
707    */
708 
709   //set brightness, contrast and gamma
710   QgsBrightnessContrastFilter *brightnessFilter = mRasterLayer->brightnessFilter();
711   if ( brightnessFilter )
712   {
713     mSliderBrightness->setValue( brightnessFilter->brightness() );
714     mSliderContrast->setValue( brightnessFilter->contrast() );
715     mGammaSpinBox->setValue( brightnessFilter->gamma() );
716   }
717 
718   // Hue and saturation color control
719   const QgsHueSaturationFilter *hueSaturationFilter = mRasterLayer->hueSaturationFilter();
720   //set hue and saturation controls to current values
721   if ( hueSaturationFilter )
722   {
723     sliderSaturation->setValue( hueSaturationFilter->saturation() );
724     comboGrayscale->setCurrentIndex( ( int ) hueSaturationFilter->grayscaleMode() );
725 
726     // Set state of saturation controls based on grayscale mode choice
727     toggleSaturationControls( static_cast<int>( hueSaturationFilter->grayscaleMode() ) );
728 
729     // Set state of colorize controls
730     mColorizeCheck->setChecked( hueSaturationFilter->colorizeOn() );
731     btnColorizeColor->setColor( hueSaturationFilter->colorizeColor() );
732     toggleColorizeControls( hueSaturationFilter->colorizeOn() );
733     sliderColorizeStrength->setValue( hueSaturationFilter->colorizeStrength() );
734     mInvertColorsCheck->setChecked( hueSaturationFilter->invertColors() );
735   }
736 
737 
738   mRefreshLayerCheckBox->setChecked( mRasterLayer->hasAutoRefreshEnabled() );
739   mRefreshLayerIntervalSpinBox->setEnabled( mRasterLayer->hasAutoRefreshEnabled() );
740   mRefreshLayerIntervalSpinBox->setValue( mRasterLayer->autoRefreshInterval() / 1000.0 );
741 
742   QgsDebugMsgLevel( QStringLiteral( "populate general tab" ), 3 );
743   /*
744    * General Tab
745    */
746 
747   mLayerOrigNameLineEd->setText( mRasterLayer->name() );
748 
749   QgsDebugMsgLevel( QStringLiteral( "populate metadata tab" ), 2 );
750   /*
751    * Metadata Tab
752    */
753   //populate the metadata tab's text browser widget with gdal metadata info
754   updateInformationContent();
755 
756   // WMS Name as layer short name
757   mLayerShortNameLineEdit->setText( mRasterLayer->shortName() );
758   // WMS Name validator
759   QValidator *shortNameValidator = new QRegularExpressionValidator( QgsApplication::shortNameRegularExpression(), this );
760   mLayerShortNameLineEdit->setValidator( shortNameValidator );
761 
762   //layer title and abstract
763   mLayerTitleLineEdit->setText( mRasterLayer->title() );
764   mLayerAbstractTextEdit->setPlainText( mRasterLayer->abstract() );
765   mLayerKeywordListLineEdit->setText( mRasterLayer->keywordList() );
766   mLayerDataUrlLineEdit->setText( mRasterLayer->dataUrl() );
767   mLayerDataUrlFormatComboBox->setCurrentIndex(
768     mLayerDataUrlFormatComboBox->findText(
769       mRasterLayer->dataUrlFormat()
770     )
771   );
772 
773   //layer attribution
774   mLayerAttributionLineEdit->setText( mRasterLayer->attribution() );
775   mLayerAttributionUrlLineEdit->setText( mRasterLayer->attributionUrl() );
776 
777   // layer metadata url
778   const QList<QgsMapLayerServerProperties::MetadataUrl> &metaUrls = mRasterLayer->serverProperties()->metadataUrls();
779   for ( const QgsMapLayerServerProperties::MetadataUrl &metaUrl : metaUrls )
780   {
781     const int row = mMetadataUrlModel->rowCount();
782     mMetadataUrlModel->setItem( row, 0, new QStandardItem( metaUrl.url ) );
783     mMetadataUrlModel->setItem( row, 1, new QStandardItem( metaUrl.type ) );
784     mMetadataUrlModel->setItem( row, 2, new QStandardItem( metaUrl.format ) );
785   }
786 
787   // layer legend url
788   mLayerLegendUrlLineEdit->setText( mRasterLayer->legendUrl() );
789   mLayerLegendUrlFormatComboBox->setCurrentIndex( mLayerLegendUrlFormatComboBox->findText( mRasterLayer->legendUrlFormat() ) );
790 
791   //WMS print layer
792   QVariant wmsPrintLayer = mRasterLayer->customProperty( QStringLiteral( "WMSPrintLayer" ) );
793   if ( wmsPrintLayer.isValid() )
794   {
795     mWMSPrintLayerLineEdit->setText( wmsPrintLayer.toString() );
796   }
797 
798   QVariant wmsPublishDataSourceUrl = mRasterLayer->customProperty( QStringLiteral( "WMSPublishDataSourceUrl" ), false );
799   mPublishDataSourceUrlCheckBox->setChecked( wmsPublishDataSourceUrl.toBool() );
800 
801   QVariant wmsBackgroundLayer = mRasterLayer->customProperty( QStringLiteral( "WMSBackgroundLayer" ), false );
802   mBackgroundLayerCheckBox->setChecked( wmsBackgroundLayer.toBool() );
803 
804   mLegendPlaceholderWidget->setLastPathSettingsKey( QStringLiteral( "lastLegendPlaceholderDir" ) );
805   mLegendPlaceholderWidget->setSource( mRasterLayer->legendPlaceholderImage() );
806   mLegendConfigEmbeddedWidget->setLayer( mRasterLayer );
807 
808   mTemporalWidget->syncToLayer();
809 
810   mPropertyCollection = mRasterLayer->pipe()->dataDefinedProperties();
811   updateDataDefinedButtons();
812 
813   for ( QgsMapLayerConfigWidget *page : std::as_const( mLayerPropertiesPages ) )
814   {
815     page->syncToLayer( mRasterLayer );
816   }
817 
818 }
819 
apply()820 void QgsRasterLayerProperties::apply()
821 {
822   if ( mSourceWidget )
823   {
824     const QString newSource = mSourceWidget->sourceUri();
825     if ( newSource != mRasterLayer->source() )
826     {
827       mRasterLayer->setDataSource( newSource, mRasterLayer->name(), mRasterLayer->providerType(), QgsDataProvider::ProviderOptions() );
828     }
829   }
830 
831   // Do nothing on "bad" layers
832   if ( !mRasterLayer->isValid() )
833     return;
834 
835   /*
836    * Legend Tab
837    */
838   mRasterLayer->setLegendPlaceholderImage( mLegendPlaceholderWidget->source() );
839   mLegendConfigEmbeddedWidget->applyToLayer();
840 
841   QgsDebugMsgLevel( QStringLiteral( "apply processing symbology tab" ), 3 );
842   /*
843    * Symbology Tab
844    */
845 
846   //set whether the layer histogram should be inverted
847   //mRasterLayer->setInvertHistogram( cboxInvertColorMap->isChecked() );
848 
849   mRasterLayer->brightnessFilter()->setBrightness( mSliderBrightness->value() );
850   mRasterLayer->brightnessFilter()->setContrast( mSliderContrast->value() );
851   mRasterLayer->brightnessFilter()->setGamma( mGammaSpinBox->value() );
852 
853   QgsDebugMsgLevel( QStringLiteral( "processing transparency tab" ), 3 );
854   /*
855    * Transparent Pixel Tab
856    */
857 
858   //set NoDataValue
859   QgsRasterRangeList myNoDataRangeList;
860   if ( "" != mRasterTransparencyWidget->leNoDataValue->text() )
861   {
862     bool myDoubleOk = false;
863     double myNoDataValue = QgsDoubleValidator::toDouble( mRasterTransparencyWidget->leNoDataValue->text(), &myDoubleOk );
864     if ( myDoubleOk )
865     {
866       QgsRasterRange myNoDataRange( myNoDataValue, myNoDataValue );
867       myNoDataRangeList << myNoDataRange;
868     }
869   }
870   for ( int bandNo = 1; bandNo <= mRasterLayer->dataProvider()->bandCount(); bandNo++ )
871   {
872     mRasterLayer->dataProvider()->setUserNoDataValue( bandNo, myNoDataRangeList );
873     mRasterLayer->dataProvider()->setUseSourceNoDataValue( bandNo, mRasterTransparencyWidget->mSrcNoDataValueCheckBox->isChecked() );
874   }
875 
876   //set renderer from widget
877   QgsRasterRendererWidget *rendererWidget = dynamic_cast<QgsRasterRendererWidget *>( mRendererStackedWidget->currentWidget() );
878   if ( rendererWidget )
879   {
880     rendererWidget->doComputations();
881 
882     mRasterLayer->setRenderer( rendererWidget->renderer() );
883   }
884 
885   mBackupCrs = mRasterLayer->crs();
886   mMetadataWidget->acceptMetadata();
887   mMetadataFilled = false;
888 
889   //transparency settings
890   QgsRasterRenderer *rasterRenderer = mRasterLayer->renderer();
891   if ( rasterRenderer )
892   {
893     rasterRenderer->setAlphaBand( mRasterTransparencyWidget->cboxTransparencyBand->currentBand() );
894     rasterRenderer->setNodataColor( mRasterTransparencyWidget->mNodataColorButton->color() );
895 
896     //Walk through each row in table and test value. If not valid set to 0.0 and continue building transparency list
897     QgsRasterTransparency *rasterTransparency = new QgsRasterTransparency();
898     if ( mRasterTransparencyWidget->tableTransparency->columnCount() == 4 )
899     {
900       QgsRasterTransparency::TransparentThreeValuePixel myTransparentPixel;
901       QList<QgsRasterTransparency::TransparentThreeValuePixel> myTransparentThreeValuePixelList;
902       for ( int myListRunner = 0; myListRunner < mRasterTransparencyWidget->tableTransparency->rowCount(); myListRunner++ )
903       {
904         myTransparentPixel.red = transparencyCellValue( myListRunner, 0 );
905         myTransparentPixel.green = transparencyCellValue( myListRunner, 1 );
906         myTransparentPixel.blue = transparencyCellValue( myListRunner, 2 );
907         myTransparentPixel.percentTransparent = transparencyCellValue( myListRunner, 3 );
908         myTransparentThreeValuePixelList.append( myTransparentPixel );
909       }
910       rasterTransparency->setTransparentThreeValuePixelList( myTransparentThreeValuePixelList );
911     }
912     else if ( mRasterTransparencyWidget->tableTransparency->columnCount() == 3 )
913     {
914       QgsRasterTransparency::TransparentSingleValuePixel myTransparentPixel;
915       QList<QgsRasterTransparency::TransparentSingleValuePixel> myTransparentSingleValuePixelList;
916       for ( int myListRunner = 0; myListRunner < mRasterTransparencyWidget->tableTransparency->rowCount(); myListRunner++ )
917       {
918         myTransparentPixel.min = transparencyCellValue( myListRunner, 0 );
919         myTransparentPixel.max = transparencyCellValue( myListRunner, 1 );
920         myTransparentPixel.percentTransparent = transparencyCellValue( myListRunner, 2 );
921 
922         myTransparentSingleValuePixelList.append( myTransparentPixel );
923       }
924       rasterTransparency->setTransparentSingleValuePixelList( myTransparentSingleValuePixelList );
925     }
926 
927     rasterRenderer->setRasterTransparency( rasterTransparency );
928 
929     // Sync the layer styling widget
930     mRasterLayer->emitStyleChanged();
931 
932     //set global transparency
933     rasterRenderer->setOpacity( mRasterTransparencyWidget->mOpacityWidget->opacity() );
934   }
935 
936   QgsDebugMsgLevel( QStringLiteral( "processing general tab" ), 3 );
937   /*
938    * General Tab
939    */
940   mRasterLayer->setName( mLayerOrigNameLineEd->text() );
941 
942   // set up the scale based layer visibility stuff....
943   mRasterLayer->setScaleBasedVisibility( chkUseScaleDependentRendering->isChecked() );
944   mRasterLayer->setMinimumScale( mScaleRangeWidget->minimumScale() );
945   mRasterLayer->setMaximumScale( mScaleRangeWidget->maximumScale() );
946 
947   mRasterLayer->setAutoRefreshInterval( mRefreshLayerIntervalSpinBox->value() * 1000.0 );
948   mRasterLayer->setAutoRefreshEnabled( mRefreshLayerCheckBox->isChecked() );
949 
950   //update the legend pixmap
951   // pixmapLegend->setPixmap( mRasterLayer->legendAsPixmap() );
952   // pixmapLegend->setScaledContents( true );
953   // pixmapLegend->repaint();
954 
955   mResamplingUtils.refreshLayerFromWidgets();
956 
957   // Hue and saturation controls
958   QgsHueSaturationFilter *hueSaturationFilter = mRasterLayer->hueSaturationFilter();
959   if ( hueSaturationFilter )
960   {
961     hueSaturationFilter->setSaturation( sliderSaturation->value() );
962     hueSaturationFilter->setGrayscaleMode( ( QgsHueSaturationFilter::GrayscaleMode ) comboGrayscale->currentIndex() );
963     hueSaturationFilter->setColorizeOn( mColorizeCheck->checkState() );
964     hueSaturationFilter->setColorizeColor( btnColorizeColor->color() );
965     hueSaturationFilter->setColorizeStrength( sliderColorizeStrength->value() );
966     hueSaturationFilter->setInvertColors( mInvertColorsCheck->isChecked() );
967   }
968 
969   //set the blend mode for the layer
970   mRasterLayer->setBlendMode( mBlendModeComboBox->blendMode() );
971 
972   // Update temporal properties
973   mTemporalWidget->saveTemporalProperties();
974 
975   mRasterLayer->setCrs( mCrsSelector->crs() );
976 
977   if ( mRasterLayer->shortName() != mLayerShortNameLineEdit->text() )
978     mMetadataFilled = false;
979   mRasterLayer->setShortName( mLayerShortNameLineEdit->text() );
980 
981   if ( mRasterLayer->title() != mLayerTitleLineEdit->text() )
982     mMetadataFilled = false;
983   mRasterLayer->setTitle( mLayerTitleLineEdit->text() );
984 
985   if ( mRasterLayer->abstract() != mLayerAbstractTextEdit->toPlainText() )
986     mMetadataFilled = false;
987   mRasterLayer->setAbstract( mLayerAbstractTextEdit->toPlainText() );
988 
989   if ( mRasterLayer->keywordList() != mLayerKeywordListLineEdit->text() )
990     mMetadataFilled = false;
991   mRasterLayer->setKeywordList( mLayerKeywordListLineEdit->text() );
992 
993   if ( mRasterLayer->dataUrl() != mLayerDataUrlLineEdit->text() )
994     mMetadataFilled = false;
995   mRasterLayer->setDataUrl( mLayerDataUrlLineEdit->text() );
996 
997   if ( mRasterLayer->dataUrlFormat() != mLayerDataUrlFormatComboBox->currentText() )
998     mMetadataFilled = false;
999   mRasterLayer->setDataUrlFormat( mLayerDataUrlFormatComboBox->currentText() );
1000 
1001   //layer attribution
1002   if ( mRasterLayer->attribution() != mLayerAttributionLineEdit->text() )
1003     mMetadataFilled = false;
1004   mRasterLayer->setAttribution( mLayerAttributionLineEdit->text() );
1005 
1006   if ( mRasterLayer->attributionUrl() != mLayerAttributionUrlLineEdit->text() )
1007     mMetadataFilled = false;
1008   mRasterLayer->setAttributionUrl( mLayerAttributionUrlLineEdit->text() );
1009 
1010   // Metadata URL
1011   QList<QgsMapLayerServerProperties::MetadataUrl> metaUrls;
1012   for ( int row = 0; row < mMetadataUrlModel->rowCount() ; row++ )
1013   {
1014     QgsMapLayerServerProperties::MetadataUrl metaUrl;
1015     metaUrl.url = mMetadataUrlModel->item( row, 0 )->text();
1016     metaUrl.type = mMetadataUrlModel->item( row, 1 )->text();
1017     metaUrl.format = mMetadataUrlModel->item( row, 2 )->text();
1018     metaUrls.append( metaUrl );
1019     mMetadataFilled = false;
1020   }
1021   mRasterLayer->serverProperties()->setMetadataUrls( metaUrls );
1022 
1023   if ( mRasterLayer->legendUrl() != mLayerLegendUrlLineEdit->text() )
1024     mMetadataFilled = false;
1025   mRasterLayer->setLegendUrl( mLayerLegendUrlLineEdit->text() );
1026 
1027   if ( mRasterLayer->legendUrlFormat() != mLayerLegendUrlFormatComboBox->currentText() )
1028     mMetadataFilled = false;
1029   mRasterLayer->setLegendUrlFormat( mLayerLegendUrlFormatComboBox->currentText() );
1030 
1031   if ( !mWMSPrintLayerLineEdit->text().isEmpty() )
1032   {
1033     mRasterLayer->setCustomProperty( QStringLiteral( "WMSPrintLayer" ), mWMSPrintLayerLineEdit->text() );
1034   }
1035 
1036   mRasterLayer->setCustomProperty( "WMSPublishDataSourceUrl", mPublishDataSourceUrlCheckBox->isChecked() );
1037   mRasterLayer->setCustomProperty( "WMSBackgroundLayer", mBackgroundLayerCheckBox->isChecked() );
1038 
1039   mRasterLayer->pipe()->setDataDefinedProperties( mPropertyCollection );
1040 
1041   // Force a redraw of the legend
1042   mRasterLayer->setLegend( QgsMapLayerLegend::defaultRasterLegend( mRasterLayer ) );
1043 
1044   //make sure the layer is redrawn
1045   mRasterLayer->triggerRepaint();
1046 
1047   // notify the project we've made a change
1048   QgsProject::instance()->setDirty( true );
1049 }
1050 
buttonBuildPyramids_clicked()1051 void QgsRasterLayerProperties::buttonBuildPyramids_clicked()
1052 {
1053   QgsRasterDataProvider *provider = mRasterLayer->dataProvider();
1054 
1055   std::unique_ptr< QgsRasterBlockFeedback > feedback( new QgsRasterBlockFeedback() );
1056 
1057   connect( feedback.get(), &QgsRasterBlockFeedback::progressChanged, mPyramidProgress, &QProgressBar::setValue );
1058   //
1059   // Go through the list marking any files that are selected in the listview
1060   // as true so that we can generate pyramids for them.
1061   //
1062   QList< QgsRasterPyramid> myPyramidList = provider->buildPyramidList();
1063   for ( int myCounterInt = 0; myCounterInt < lbxPyramidResolutions->count(); myCounterInt++ )
1064   {
1065     QListWidgetItem *myItem = lbxPyramidResolutions->item( myCounterInt );
1066     //mark to be pyramided
1067     myPyramidList[myCounterInt].setBuild( myItem->isSelected() || myPyramidList[myCounterInt].getExists() );
1068   }
1069 
1070   // keep it in sync with qgsrasterpyramidsoptionwidget.cpp
1071   QString prefix = provider->name() + "/driverOptions/_pyramids/";
1072   QgsSettings mySettings;
1073   QString resamplingMethod( cboResamplingMethod->currentData().toString() );
1074   mySettings.setValue( prefix + "resampling", resamplingMethod );
1075 
1076   //
1077   // Ask raster layer to build the pyramids
1078   //
1079 
1080   // let the user know we're going to possibly be taking a while
1081   QApplication::setOverrideCursor( Qt::WaitCursor );
1082   QString res = provider->buildPyramids(
1083                   myPyramidList,
1084                   resamplingMethod,
1085                   ( QgsRaster::RasterPyramidsFormat ) cbxPyramidsFormat->currentIndex(),
1086                   QStringList(),
1087                   feedback.get() );
1088   QApplication::restoreOverrideCursor();
1089   mPyramidProgress->setValue( 0 );
1090   buttonBuildPyramids->setEnabled( false );
1091   if ( !res.isNull() )
1092   {
1093     if ( res == QLatin1String( "CANCELED" ) )
1094     {
1095       // user canceled
1096     }
1097     else if ( res == QLatin1String( "ERROR_WRITE_ACCESS" ) )
1098     {
1099       QMessageBox::warning( this, tr( "Building Pyramids" ),
1100                             tr( "Write access denied. Adjust the file permissions and try again." ) );
1101     }
1102     else if ( res == QLatin1String( "ERROR_WRITE_FORMAT" ) )
1103     {
1104       QMessageBox::warning( this, tr( "Building Pyramids" ),
1105                             tr( "The file was not writable. Some formats do not "
1106                                 "support pyramid overviews. Consult the GDAL documentation if in doubt." ) );
1107     }
1108     else if ( res == QLatin1String( "FAILED_NOT_SUPPORTED" ) )
1109     {
1110       QMessageBox::warning( this, tr( "Building Pyramids" ),
1111                             tr( "Building pyramid overviews is not supported on this type of raster." ) );
1112     }
1113     else if ( res == QLatin1String( "ERROR_JPEG_COMPRESSION" ) )
1114     {
1115       QMessageBox::warning( this, tr( "Building Pyramids" ),
1116                             tr( "Building internal pyramid overviews is not supported on raster layers with JPEG compression and your current libtiff library." ) );
1117     }
1118     else if ( res == QLatin1String( "ERROR_VIRTUAL" ) )
1119     {
1120       QMessageBox::warning( this, tr( "Building Pyramids" ),
1121                             tr( "Building pyramid overviews is not supported on this type of raster." ) );
1122     }
1123 
1124   }
1125 
1126   //
1127   // repopulate the pyramids list
1128   //
1129   lbxPyramidResolutions->clear();
1130   // Need to rebuild list as some or all pyramids may have failed to build
1131   myPyramidList = provider->buildPyramidList();
1132   QIcon myPyramidPixmap( QgsApplication::getThemeIcon( "/mIconPyramid.svg" ) );
1133   QIcon myNoPyramidPixmap( QgsApplication::getThemeIcon( "/mIconNoPyramid.svg" ) );
1134 
1135   for ( const QgsRasterPyramid &pyramid : std::as_const( myPyramidList ) )
1136   {
1137     if ( pyramid.getExists() )
1138     {
1139       lbxPyramidResolutions->addItem( new QListWidgetItem( myPyramidPixmap,
1140                                       QString::number( pyramid.getXDim() ) + QStringLiteral( " x " ) +
1141                                       QString::number( pyramid.getYDim() ) ) );
1142     }
1143     else
1144     {
1145       lbxPyramidResolutions->addItem( new QListWidgetItem( myNoPyramidPixmap,
1146                                       QString::number( pyramid.getXDim() ) + QStringLiteral( " x " ) +
1147                                       QString::number( pyramid.getYDim() ) ) );
1148     }
1149   }
1150   //update the legend pixmap
1151   // pixmapLegend->setPixmap( mRasterLayer->legendAsPixmap() );
1152   // pixmapLegend->setScaledContents( true );
1153   // pixmapLegend->repaint();
1154 
1155   //populate the metadata tab's text browser widget with gdal metadata info
1156   updateInformationContent();
1157 }
1158 
urlClicked(const QUrl & url)1159 void QgsRasterLayerProperties::urlClicked( const QUrl &url )
1160 {
1161   QFileInfo file( url.toLocalFile() );
1162   if ( file.exists() && !file.isDir() )
1163     QgsGui::instance()->nativePlatformInterface()->openFileExplorerAndSelectFile( url.toLocalFile() );
1164   else
1165     QDesktopServices::openUrl( url );
1166 }
1167 
mRenderTypeComboBox_currentIndexChanged(int index)1168 void QgsRasterLayerProperties::mRenderTypeComboBox_currentIndexChanged( int index )
1169 {
1170   if ( index < 0 || mDisableRenderTypeComboBoxCurrentIndexChanged || ! mRasterLayer->renderer() )
1171   {
1172     return;
1173   }
1174 
1175   QString rendererName = mRenderTypeComboBox->itemData( index ).toString();
1176   setRendererWidget( rendererName );
1177 }
1178 
mCrsSelector_crsChanged(const QgsCoordinateReferenceSystem & crs)1179 void QgsRasterLayerProperties::mCrsSelector_crsChanged( const QgsCoordinateReferenceSystem &crs )
1180 {
1181   QgsDatumTransformDialog::run( crs, QgsProject::instance()->crs(), this, mMapCanvas, tr( "Select Transformation" ) );
1182   mRasterLayer->setCrs( crs );
1183   mMetadataWidget->crsChanged();
1184 }
1185 
setTransparencyCell(int row,int column,double value)1186 void QgsRasterLayerProperties::setTransparencyCell( int row, int column, double value )
1187 {
1188   QgsDebugMsgLevel( QStringLiteral( "value = %1" ).arg( value, 0, 'g', 17 ), 3 );
1189   QgsRasterDataProvider *provider = mRasterLayer->dataProvider();
1190   if ( !provider ) return;
1191 
1192   QgsRasterRenderer *renderer = mRendererWidget->renderer();
1193   if ( !renderer ) return;
1194   int nBands = renderer->usesBands().size();
1195 
1196   QLineEdit *lineEdit = new QLineEdit();
1197   lineEdit->setFrame( false ); // frame looks bad in table
1198   // Without margins row selection is not displayed (important for delete row)
1199   lineEdit->setContentsMargins( 1, 1, 1, 1 );
1200 
1201   if ( column == mRasterTransparencyWidget->tableTransparency->columnCount() - 1 )
1202   {
1203     // transparency
1204     // Who needs transparency as floating point?
1205     lineEdit->setValidator( new QIntValidator( nullptr ) );
1206     lineEdit->setText( QString::number( static_cast<int>( value ) ) );
1207   }
1208   else
1209   {
1210     // value
1211     QString valueString;
1212     switch ( provider->sourceDataType( 1 ) )
1213     {
1214       case Qgis::DataType::Float32:
1215       case Qgis::DataType::Float64:
1216         lineEdit->setValidator( new QgsDoubleValidator( nullptr ) );
1217         if ( !std::isnan( value ) )
1218         {
1219           double v = QgsRasterBlock::printValue( value ).toDouble();
1220           valueString = QLocale().toString( v, 'g' ) ;
1221         }
1222         break;
1223       default:
1224         lineEdit->setValidator( new QIntValidator( nullptr ) );
1225         if ( !std::isnan( value ) )
1226         {
1227           valueString = QLocale().toString( static_cast<int>( value ) );
1228         }
1229         break;
1230     }
1231     lineEdit->setText( valueString );
1232   }
1233   mRasterTransparencyWidget->tableTransparency->setCellWidget( row, column, lineEdit );
1234   adjustTransparencyCellWidth( row, column );
1235 
1236   if ( nBands == 1 && ( column == 0 || column == 1 ) )
1237   {
1238     connect( lineEdit, &QLineEdit::textEdited, this, &QgsRasterLayerProperties::transparencyCellTextEdited );
1239   }
1240   mRasterTransparencyWidget->tableTransparency->resizeColumnsToContents();
1241 }
1242 
setTransparencyCellValue(int row,int column,double value)1243 void QgsRasterLayerProperties::setTransparencyCellValue( int row, int column, double value )
1244 {
1245   QLineEdit *lineEdit = dynamic_cast<QLineEdit *>( mRasterTransparencyWidget->tableTransparency->cellWidget( row, column ) );
1246   if ( !lineEdit ) return;
1247   double v = QgsRasterBlock::printValue( value ).toDouble();
1248   lineEdit->setText( QLocale().toString( v, 'g' ) );
1249   lineEdit->adjustSize();
1250   adjustTransparencyCellWidth( row, column );
1251   mRasterTransparencyWidget->tableTransparency->resizeColumnsToContents();
1252 }
1253 
transparencyCellValue(int row,int column)1254 double QgsRasterLayerProperties::transparencyCellValue( int row, int column )
1255 {
1256   QLineEdit *lineEdit = dynamic_cast<QLineEdit *>( mRasterTransparencyWidget->tableTransparency->cellWidget( row, column ) );
1257   if ( !lineEdit || lineEdit->text().isEmpty() )
1258   {
1259     return std::numeric_limits<double>::quiet_NaN();
1260   }
1261   return QLocale().toDouble( lineEdit->text() );
1262 }
1263 
adjustTransparencyCellWidth(int row,int column)1264 void QgsRasterLayerProperties::adjustTransparencyCellWidth( int row, int column )
1265 {
1266   QLineEdit *lineEdit = dynamic_cast<QLineEdit *>( mRasterTransparencyWidget->tableTransparency->cellWidget( row, column ) );
1267   if ( !lineEdit ) return;
1268 
1269   int width = std::max( lineEdit->fontMetrics().boundingRect( lineEdit->text() ).width() + 10, 100 );
1270   width = std::max( width, mRasterTransparencyWidget->tableTransparency->columnWidth( column ) );
1271 
1272   lineEdit->setFixedWidth( width );
1273 }
1274 
transparencyCellTextEdited(const QString & text)1275 void QgsRasterLayerProperties::transparencyCellTextEdited( const QString &text )
1276 {
1277   Q_UNUSED( text )
1278   QgsDebugMsgLevel( QStringLiteral( "text = %1" ).arg( text ), 3 );
1279   QgsRasterRenderer *renderer = mRendererWidget->renderer();
1280   if ( !renderer )
1281   {
1282     return;
1283   }
1284   int nBands = renderer->usesBands().size();
1285   if ( nBands == 1 )
1286   {
1287     QLineEdit *lineEdit = qobject_cast<QLineEdit *>( sender() );
1288     if ( !lineEdit ) return;
1289     int row = -1;
1290     int column = -1;
1291     for ( int r = 0; r < mRasterTransparencyWidget->tableTransparency->rowCount(); r++ )
1292     {
1293       for ( int c = 0; c < mRasterTransparencyWidget->tableTransparency->columnCount(); c++ )
1294       {
1295         if ( mRasterTransparencyWidget->tableTransparency->cellWidget( r, c ) == sender() )
1296         {
1297           row = r;
1298           column = c;
1299           break;
1300         }
1301       }
1302       if ( row != -1 ) break;
1303     }
1304     QgsDebugMsgLevel( QStringLiteral( "row = %1 column =%2" ).arg( row ).arg( column ), 3 );
1305 
1306     if ( column == 0 )
1307     {
1308       QLineEdit *toLineEdit = dynamic_cast<QLineEdit *>( mRasterTransparencyWidget->tableTransparency->cellWidget( row, 1 ) );
1309       if ( !toLineEdit ) return;
1310       bool toChanged = mTransparencyToEdited.value( row );
1311       QgsDebugMsgLevel( QStringLiteral( "toChanged = %1" ).arg( toChanged ), 3 );
1312       if ( !toChanged )
1313       {
1314         toLineEdit->setText( lineEdit->text() );
1315       }
1316     }
1317     else if ( column == 1 )
1318     {
1319       setTransparencyToEdited( row );
1320     }
1321   }
1322 }
1323 
aboutToShowStyleMenu()1324 void QgsRasterLayerProperties::aboutToShowStyleMenu()
1325 {
1326   // this should be unified with QgsVectorLayerProperties::aboutToShowStyleMenu()
1327 
1328   QMenu *m = qobject_cast<QMenu *>( sender() );
1329 
1330   QgsMapLayerStyleGuiUtils::instance()->removesExtraMenuSeparators( m );
1331   // re-add style manager actions!
1332   m->addSeparator();
1333   QgsMapLayerStyleGuiUtils::instance()->addStyleManagerActions( m, mRasterLayer );
1334 }
1335 
syncToLayer()1336 void QgsRasterLayerProperties::syncToLayer()
1337 {
1338   QgsRasterRenderer *renderer = mRasterLayer->renderer();
1339   if ( renderer )
1340   {
1341     setRendererWidget( renderer->type() );
1342   }
1343   sync();
1344   mRasterLayer->triggerRepaint();
1345 }
1346 
setTransparencyToEdited(int row)1347 void QgsRasterLayerProperties::setTransparencyToEdited( int row )
1348 {
1349   if ( row >= mTransparencyToEdited.size() )
1350   {
1351     mTransparencyToEdited.resize( row + 1 );
1352   }
1353   mTransparencyToEdited[row] = true;
1354 }
1355 
optionsStackedWidget_CurrentChanged(int index)1356 void QgsRasterLayerProperties::optionsStackedWidget_CurrentChanged( int index )
1357 {
1358   QgsOptionsDialogBase::optionsStackedWidget_CurrentChanged( index );
1359 
1360   bool isMetadataPanel = ( index == mOptStackedWidget->indexOf( mOptsPage_Metadata ) );
1361   mBtnStyle->setVisible( ! isMetadataPanel );
1362   mBtnMetadata->setVisible( isMetadataPanel );
1363 
1364   if ( !mHistogramWidget )
1365     return;
1366 
1367   if ( index == mOptStackedWidget->indexOf( mOptsPage_Histogram ) )
1368   {
1369     mHistogramWidget->setActive( true );
1370   }
1371   else
1372   {
1373     mHistogramWidget->setActive( false );
1374   }
1375 
1376   if ( index == mOptStackedWidget->indexOf( mOptsPage_Information ) || !mMetadataFilled )
1377   {
1378     //set the metadata contents (which can be expensive)
1379     updateInformationContent();
1380   }
1381 }
1382 
initializeDataDefinedButton(QgsPropertyOverrideButton * button,QgsRasterPipe::Property key)1383 void QgsRasterLayerProperties::initializeDataDefinedButton( QgsPropertyOverrideButton *button, QgsRasterPipe::Property key )
1384 {
1385   button->blockSignals( true );
1386   button->init( key, mPropertyCollection, QgsRasterPipe::propertyDefinitions(), nullptr );
1387   connect( button, &QgsPropertyOverrideButton::changed, this, &QgsRasterLayerProperties::updateProperty );
1388   button->registerExpressionContextGenerator( this );
1389   button->blockSignals( false );
1390 }
1391 
updateDataDefinedButtons()1392 void QgsRasterLayerProperties::updateDataDefinedButtons()
1393 {
1394   const auto propertyOverrideButtons { findChildren< QgsPropertyOverrideButton * >() };
1395   for ( QgsPropertyOverrideButton *button : propertyOverrideButtons )
1396   {
1397     updateDataDefinedButton( button );
1398   }
1399 }
1400 
updateDataDefinedButton(QgsPropertyOverrideButton * button)1401 void QgsRasterLayerProperties::updateDataDefinedButton( QgsPropertyOverrideButton *button )
1402 {
1403   if ( !button )
1404     return;
1405 
1406   if ( button->propertyKey() < 0 )
1407     return;
1408 
1409   QgsRasterPipe::Property key = static_cast< QgsRasterPipe::Property >( button->propertyKey() );
1410   whileBlocking( button )->setToProperty( mPropertyCollection.property( key ) );
1411 }
1412 
updateProperty()1413 void QgsRasterLayerProperties::updateProperty()
1414 {
1415   QgsPropertyOverrideButton *button = qobject_cast<QgsPropertyOverrideButton *>( sender() );
1416   QgsRasterPipe::Property key = static_cast<  QgsRasterPipe::Property >( button->propertyKey() );
1417   mPropertyCollection.setProperty( key, button->toProperty() );
1418 }
1419 
toggleSaturationControls(int grayscaleMode)1420 void QgsRasterLayerProperties::toggleSaturationControls( int grayscaleMode )
1421 {
1422   // Enable or disable saturation controls based on choice of grayscale mode
1423   if ( grayscaleMode == 0 )
1424   {
1425     sliderSaturation->setEnabled( true );
1426     spinBoxSaturation->setEnabled( true );
1427   }
1428   else
1429   {
1430     sliderSaturation->setEnabled( false );
1431     spinBoxSaturation->setEnabled( false );
1432   }
1433 }
1434 
toggleColorizeControls(bool colorizeEnabled)1435 void QgsRasterLayerProperties::toggleColorizeControls( bool colorizeEnabled )
1436 {
1437   // Enable or disable colorize controls based on checkbox
1438   btnColorizeColor->setEnabled( colorizeEnabled );
1439   sliderColorizeStrength->setEnabled( colorizeEnabled );
1440   spinColorizeStrength->setEnabled( colorizeEnabled );
1441 }
1442 
1443 
redGradient()1444 QLinearGradient QgsRasterLayerProperties::redGradient()
1445 {
1446   //define a gradient
1447   // TODO change this to actual polygon dims
1448   QLinearGradient myGradient = QLinearGradient( mGradientWidth, 0, mGradientWidth, mGradientHeight );
1449   myGradient.setColorAt( 0.0, QColor( 242, 14, 25, 190 ) );
1450   myGradient.setColorAt( 0.5, QColor( 175, 29, 37, 190 ) );
1451   myGradient.setColorAt( 1.0, QColor( 114, 17, 22, 190 ) );
1452   return myGradient;
1453 }
greenGradient()1454 QLinearGradient QgsRasterLayerProperties::greenGradient()
1455 {
1456   //define a gradient
1457   // TODO change this to actual polygon dims
1458   QLinearGradient myGradient = QLinearGradient( mGradientWidth, 0, mGradientWidth, mGradientHeight );
1459   myGradient.setColorAt( 0.0, QColor( 48, 168, 5, 190 ) );
1460   myGradient.setColorAt( 0.8, QColor( 36, 122, 4, 190 ) );
1461   myGradient.setColorAt( 1.0, QColor( 21, 71, 2, 190 ) );
1462   return myGradient;
1463 }
blueGradient()1464 QLinearGradient QgsRasterLayerProperties::blueGradient()
1465 {
1466   //define a gradient
1467   // TODO change this to actual polygon dims
1468   QLinearGradient myGradient = QLinearGradient( mGradientWidth, 0, mGradientWidth, mGradientHeight );
1469   myGradient.setColorAt( 0.0, QColor( 30, 0, 106, 190 ) );
1470   myGradient.setColorAt( 0.2, QColor( 30, 72, 128, 190 ) );
1471   myGradient.setColorAt( 1.0, QColor( 30, 223, 196, 190 ) );
1472   return myGradient;
1473 }
grayGradient()1474 QLinearGradient QgsRasterLayerProperties::grayGradient()
1475 {
1476   //define a gradient
1477   // TODO change this to actual polygon dims
1478   QLinearGradient myGradient = QLinearGradient( mGradientWidth, 0, mGradientWidth, mGradientHeight );
1479   myGradient.setColorAt( 0.0, QColor( 5, 5, 5, 190 ) );
1480   myGradient.setColorAt( 0.8, QColor( 122, 122, 122, 190 ) );
1481   myGradient.setColorAt( 1.0, QColor( 220, 220, 220, 190 ) );
1482   return myGradient;
1483 }
highlightGradient()1484 QLinearGradient QgsRasterLayerProperties::highlightGradient()
1485 {
1486   //define another gradient for the highlight
1487   // TODO change this to actual polygon dims
1488   QLinearGradient myGradient = QLinearGradient( mGradientWidth, 0, mGradientWidth, mGradientHeight );
1489   myGradient.setColorAt( 1.0, QColor( 255, 255, 255, 50 ) );
1490   myGradient.setColorAt( 0.5, QColor( 255, 255, 255, 100 ) );
1491   myGradient.setColorAt( 0.0, QColor( 255, 255, 255, 150 ) );
1492   return myGradient;
1493 }
1494 
addMetadataUrl()1495 void QgsRasterLayerProperties::addMetadataUrl()
1496 {
1497   const int row = mMetadataUrlModel->rowCount();
1498   mMetadataUrlModel->setItem( row, 0, new QStandardItem( QLatin1String() ) );
1499   mMetadataUrlModel->setItem( row, 1, new QStandardItem( QLatin1String() ) );
1500   mMetadataUrlModel->setItem( row, 2, new QStandardItem( QLatin1String() ) );
1501 }
1502 
removeSelectedMetadataUrl()1503 void QgsRasterLayerProperties::removeSelectedMetadataUrl()
1504 {
1505   const QModelIndexList selectedRows = tableViewMetadataUrl->selectionModel()->selectedRows();
1506   if ( selectedRows.empty() )
1507     return;
1508   mMetadataUrlModel->removeRow( selectedRows[0].row() );
1509 }
1510 
1511 
1512 //
1513 //
1514 // Next four methods for saving and restoring qml style state
1515 //
1516 //
loadDefaultStyle_clicked()1517 void QgsRasterLayerProperties::loadDefaultStyle_clicked()
1518 {
1519   bool defaultLoadedFlag = false;
1520   QString myMessage = mRasterLayer->loadDefaultStyle( defaultLoadedFlag );
1521   //reset if the default style was loaded OK only
1522   if ( defaultLoadedFlag )
1523   {
1524     syncToLayer();
1525   }
1526   else
1527   {
1528     //otherwise let the user know what went wrong
1529     QMessageBox::information( this,
1530                               tr( "Default Style" ),
1531                               myMessage
1532                             );
1533   }
1534 }
1535 
saveDefaultStyle_clicked()1536 void QgsRasterLayerProperties::saveDefaultStyle_clicked()
1537 {
1538 
1539   apply(); // make sure the style to save is up-to-date
1540 
1541   // a flag passed by reference
1542   bool defaultSavedFlag = false;
1543   // after calling this the above flag will be set true for success
1544   // or false if the save operation failed
1545   QString myMessage = mRasterLayer->saveDefaultStyle( defaultSavedFlag );
1546   if ( !defaultSavedFlag )
1547   {
1548     //let the user know what went wrong
1549     QMessageBox::information( this,
1550                               tr( "Default Style" ),
1551                               myMessage
1552                             );
1553   }
1554 }
1555 
1556 
loadStyle_clicked()1557 void QgsRasterLayerProperties::loadStyle_clicked()
1558 {
1559   QgsSettings settings;
1560   QString lastUsedDir = settings.value( QStringLiteral( "style/lastStyleDir" ), QDir::homePath() ).toString();
1561 
1562   QString fileName = QFileDialog::getOpenFileName(
1563                        this,
1564                        tr( "Load layer properties from style file" ),
1565                        lastUsedDir,
1566                        tr( "QGIS Layer Style File" ) + " (*.qml)" );
1567   if ( fileName.isEmpty() )
1568     return;
1569 
1570   // ensure the user never omits the extension from the file name
1571   if ( !fileName.endsWith( QLatin1String( ".qml" ), Qt::CaseInsensitive ) )
1572     fileName += QLatin1String( ".qml" );
1573 
1574   mOldStyle = mRasterLayer->styleManager()->style( mRasterLayer->styleManager()->currentStyle() );
1575 
1576   bool defaultLoadedFlag = false;
1577   QString message = mRasterLayer->loadNamedStyle( fileName, defaultLoadedFlag );
1578   if ( defaultLoadedFlag )
1579   {
1580     settings.setValue( QStringLiteral( "style/lastStyleDir" ), QFileInfo( fileName ).absolutePath() );
1581     syncToLayer();
1582   }
1583   else
1584   {
1585     QMessageBox::information( this, tr( "Save Style" ), message );
1586   }
1587 }
1588 
1589 
saveStyleAs_clicked()1590 void QgsRasterLayerProperties::saveStyleAs_clicked()
1591 {
1592   QgsSettings settings;
1593   QString lastUsedDir = settings.value( QStringLiteral( "style/lastStyleDir" ), QDir::homePath() ).toString();
1594 
1595   QString selectedFilter;
1596   QString outputFileName = QFileDialog::getSaveFileName(
1597                              this,
1598                              tr( "Save layer properties as style file" ),
1599                              lastUsedDir,
1600                              tr( "QGIS Layer Style File" ) + " (*.qml)" + ";;" + tr( "Styled Layer Descriptor" ) + " (*.sld)",
1601                              &selectedFilter );
1602   if ( outputFileName.isEmpty() )
1603     return;
1604 
1605   StyleType type;
1606   // use selectedFilter to set style type
1607   if ( selectedFilter.contains( QStringLiteral( ".qml" ), Qt::CaseInsensitive ) )
1608   {
1609     outputFileName = QgsFileUtils::ensureFileNameHasExtension( outputFileName, QStringList() << QStringLiteral( "qml" ) );
1610     type = StyleType::QML;
1611   }
1612   else
1613   {
1614     outputFileName = QgsFileUtils::ensureFileNameHasExtension( outputFileName, QStringList() << QStringLiteral( "sld" ) );
1615     type = StyleType::SLD;
1616   }
1617 
1618   apply(); // make sure the style to save is up-to-date
1619 
1620   // then export style
1621   bool defaultLoadedFlag = false;
1622   QString message;
1623   switch ( type )
1624   {
1625     case QML:
1626     {
1627       message = mRasterLayer->saveNamedStyle( outputFileName, defaultLoadedFlag );
1628       break;
1629     }
1630     case SLD:
1631     {
1632       message = mRasterLayer->saveSldStyle( outputFileName, defaultLoadedFlag );
1633       break;
1634     }
1635   }
1636   if ( defaultLoadedFlag )
1637   {
1638     settings.setValue( QStringLiteral( "style/lastStyleDir" ), QFileInfo( outputFileName ).absolutePath() );
1639     sync();
1640   }
1641   else
1642     QMessageBox::information( this, tr( "Save Style" ), message );
1643 }
1644 
restoreWindowModality()1645 void QgsRasterLayerProperties::restoreWindowModality()
1646 {
1647   hide();
1648   setModal( true );
1649   show();
1650   raise();
1651   activateWindow();
1652 }
1653 
1654 //
1655 //
1656 // Next four methods for saving and restoring QMD metadata
1657 //
1658 //
1659 
loadMetadata()1660 void QgsRasterLayerProperties::loadMetadata()
1661 {
1662   QgsSettings myQSettings;  // where we keep last used filter in persistent state
1663   QString myLastUsedDir = myQSettings.value( QStringLiteral( "style/lastStyleDir" ), QDir::homePath() ).toString();
1664 
1665   QString myFileName = QFileDialog::getOpenFileName( this, tr( "Load layer metadata from metadata file" ), myLastUsedDir,
1666                        tr( "QGIS Layer Metadata File" ) + " (*.qmd)" );
1667   if ( myFileName.isNull() )
1668   {
1669     return;
1670   }
1671 
1672   QString myMessage;
1673   bool defaultLoadedFlag = false;
1674   myMessage = mRasterLayer->loadNamedMetadata( myFileName, defaultLoadedFlag );
1675 
1676   //reset if the default style was loaded OK only
1677   if ( defaultLoadedFlag )
1678   {
1679     mMetadataWidget->setMetadata( &mRasterLayer->metadata() );
1680   }
1681   else
1682   {
1683     //let the user know what went wrong
1684     QMessageBox::warning( this, tr( "Load Metadata" ), myMessage );
1685   }
1686 
1687   QFileInfo myFI( myFileName );
1688   QString myPath = myFI.path();
1689   myQSettings.setValue( QStringLiteral( "style/lastStyleDir" ), myPath );
1690 
1691   activateWindow(); // set focus back to properties dialog
1692 }
1693 
saveMetadataAs()1694 void QgsRasterLayerProperties::saveMetadataAs()
1695 {
1696   QgsSettings myQSettings;  // where we keep last used filter in persistent state
1697   QString myLastUsedDir = myQSettings.value( QStringLiteral( "style/lastStyleDir" ), QDir::homePath() ).toString();
1698 
1699   QString myOutputFileName = QFileDialog::getSaveFileName( this, tr( "Save Layer Metadata as QMD" ),
1700                              myLastUsedDir, tr( "QMD File" ) + " (*.qmd)" );
1701   if ( myOutputFileName.isNull() ) //dialog canceled
1702   {
1703     return;
1704   }
1705 
1706   mMetadataWidget->acceptMetadata();
1707 
1708   //ensure the user never omitted the extension from the file name
1709   if ( !myOutputFileName.endsWith( QgsMapLayer::extensionPropertyType( QgsMapLayer::Metadata ), Qt::CaseInsensitive ) )
1710   {
1711     myOutputFileName += QgsMapLayer::extensionPropertyType( QgsMapLayer::Metadata );
1712   }
1713 
1714   bool defaultLoadedFlag = false;
1715   QString message = mRasterLayer->saveNamedMetadata( myOutputFileName, defaultLoadedFlag );
1716   if ( defaultLoadedFlag )
1717     myQSettings.setValue( QStringLiteral( "style/lastStyleDir" ), QFileInfo( myOutputFileName ).absolutePath() );
1718   else
1719     QMessageBox::information( this, tr( "Save Metadata" ), message );
1720 }
1721 
saveDefaultMetadata()1722 void QgsRasterLayerProperties::saveDefaultMetadata()
1723 {
1724   mMetadataWidget->acceptMetadata();
1725 
1726   bool defaultSavedFlag = false;
1727   QString errorMsg = mRasterLayer->saveDefaultMetadata( defaultSavedFlag );
1728   if ( !defaultSavedFlag )
1729   {
1730     QMessageBox::warning( this, tr( "Default Metadata" ), errorMsg );
1731   }
1732 }
1733 
loadDefaultMetadata()1734 void QgsRasterLayerProperties::loadDefaultMetadata()
1735 {
1736   bool defaultLoadedFlag = false;
1737   QString myMessage = mRasterLayer->loadNamedMetadata( mRasterLayer->metadataUri(), defaultLoadedFlag );
1738   //reset if the default metadata was loaded OK only
1739   if ( defaultLoadedFlag )
1740   {
1741     mMetadataWidget->setMetadata( &mRasterLayer->metadata() );
1742   }
1743   else
1744   {
1745     QMessageBox::information( this, tr( "Default Metadata" ), myMessage );
1746   }
1747 }
1748 
1749 
toggleBuildPyramidsButton()1750 void QgsRasterLayerProperties::toggleBuildPyramidsButton()
1751 {
1752   if ( lbxPyramidResolutions->selectedItems().empty() )
1753   {
1754     buttonBuildPyramids->setEnabled( false );
1755   }
1756   else
1757   {
1758     buttonBuildPyramids->setEnabled( true );
1759   }
1760 }
1761 
mResetColorRenderingBtn_clicked()1762 void QgsRasterLayerProperties::mResetColorRenderingBtn_clicked()
1763 {
1764   mBlendModeComboBox->setBlendMode( QPainter::CompositionMode_SourceOver );
1765   mSliderBrightness->setValue( 0 );
1766   mSliderContrast->setValue( 0 );
1767   mGammaSpinBox->setValue( 1.0 );
1768   sliderSaturation->setValue( 0 );
1769   comboGrayscale->setCurrentIndex( ( int ) QgsHueSaturationFilter::GrayscaleOff );
1770   mColorizeCheck->setChecked( false );
1771   sliderColorizeStrength->setValue( 100 );
1772   mInvertColorsCheck->setChecked( false );
1773 }
1774 
rasterIsMultiBandColor()1775 bool QgsRasterLayerProperties::rasterIsMultiBandColor()
1776 {
1777   return mRasterLayer && nullptr != dynamic_cast<QgsMultiBandColorRenderer *>( mRasterLayer->renderer() );
1778 }
1779 
updateInformationContent()1780 void QgsRasterLayerProperties::updateInformationContent()
1781 {
1782   const QString myStyle = QgsApplication::reportStyleSheet( QgsApplication::StyleSheetType::WebBrowser );
1783   // Inject the stylesheet
1784   const QString html { mRasterLayer->htmlMetadata().replace( QLatin1String( "<head>" ), QStringLiteral( R"raw(<head><style type="text/css">%1</style>)raw" ) ).arg( myStyle ) };
1785   mMetadataViewer->setHtml( html );
1786   mMetadataFilled = true;
1787 }
1788 
onCancel()1789 void QgsRasterLayerProperties::onCancel()
1790 {
1791   if ( mOldStyle.xmlData() != mRasterLayer->styleManager()->style( mRasterLayer->styleManager()->currentStyle() ).xmlData() )
1792   {
1793     // need to reset style to previous - style applied directly to the layer (not in apply())
1794     QString myMessage;
1795     QDomDocument doc( QStringLiteral( "qgis" ) );
1796     int errorLine, errorColumn;
1797     doc.setContent( mOldStyle.xmlData(), false, &myMessage, &errorLine, &errorColumn );
1798     mRasterLayer->importNamedStyle( doc, myMessage );
1799     syncToLayer();
1800   }
1801   if ( mBackupCrs != mRasterLayer->crs() )
1802     mRasterLayer->setCrs( mBackupCrs );
1803 }
1804 
showHelp()1805 void QgsRasterLayerProperties::showHelp()
1806 {
1807   const QVariant helpPage = mOptionsStackedWidget->currentWidget()->property( "helpPage" );
1808 
1809   if ( helpPage.isValid() )
1810   {
1811     QgsHelp::openHelp( helpPage.toString() );
1812   }
1813   else
1814   {
1815     QgsHelp::openHelp( QStringLiteral( "working_with_raster/raster_properties.html" ) );
1816   }
1817 }
1818 
updateGammaSpinBox(int value)1819 void QgsRasterLayerProperties::updateGammaSpinBox( int value )
1820 {
1821   whileBlocking( mGammaSpinBox )->setValue( value / 100.0 );
1822 }
1823 
updateGammaSlider(double value)1824 void QgsRasterLayerProperties::updateGammaSlider( double value )
1825 {
1826   whileBlocking( mSliderGamma )->setValue( value * 100 );
1827 }
1828