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