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