26#include <QInputDialog>
29#include <QContextMenuEvent>
34QMap< QString, QStringList > QgsRasterFormatSaveOptionsWidget::sBuiltinProfiles;
36static const QString PYRAMID_JPEG_YCBCR_COMPRESSION( QStringLiteral(
"JPEG_QUALITY_OVERVIEW=75 COMPRESS_OVERVIEW=JPEG PHOTOMETRIC_OVERVIEW=YCBCR INTERLEAVE_OVERVIEW=PIXEL" ) );
37static const QString PYRAMID_JPEG_COMPRESSION( QStringLiteral(
"JPEG_QUALITY_OVERVIEW=75 COMPRESS_OVERVIEW=JPEG INTERLEAVE_OVERVIEW=PIXEL" ) );
43 , mProvider( provider )
48 mOptionsTable->setMinimumSize( 200, mOptionsTable->verticalHeader()->defaultSectionSize() * 4 + mOptionsTable->horizontalHeader()->height() + 2 );
50 connect( mProfileNewButton, &QPushButton::clicked,
this, &QgsRasterFormatSaveOptionsWidget::mProfileNewButton_clicked );
51 connect( mProfileDeleteButton, &QPushButton::clicked,
this, &QgsRasterFormatSaveOptionsWidget::mProfileDeleteButton_clicked );
52 connect( mProfileResetButton, &QPushButton::clicked,
this, &QgsRasterFormatSaveOptionsWidget::mProfileResetButton_clicked );
53 connect( mOptionsAddButton, &QPushButton::clicked,
this, &QgsRasterFormatSaveOptionsWidget::mOptionsAddButton_clicked );
54 connect( mOptionsDeleteButton, &QPushButton::clicked,
this, &QgsRasterFormatSaveOptionsWidget::mOptionsDeleteButton_clicked );
55 connect( mOptionsLineEdit, &QLineEdit::editingFinished,
this, &QgsRasterFormatSaveOptionsWidget::mOptionsLineEdit_editingFinished );
59 if ( sBuiltinProfiles.isEmpty() )
62 sBuiltinProfiles[ QStringLiteral(
"z_adefault" )] = ( QStringList() << QString() << tr(
"Default" ) << QString() );
67 sBuiltinProfiles[ QStringLiteral(
"z_gtiff_1big" )] =
68 ( QStringList() << QStringLiteral(
"GTiff" ) << tr(
"No Compression" )
69 << QStringLiteral(
"COMPRESS=NONE BIGTIFF=IF_NEEDED" ) );
70 sBuiltinProfiles[ QStringLiteral(
"z_gtiff_2medium" )] =
71 ( QStringList() << QStringLiteral(
"GTiff" ) << tr(
"Low Compression" )
72 << QStringLiteral(
"COMPRESS=PACKBITS" ) );
73 sBuiltinProfiles[ QStringLiteral(
"z_gtiff_3small" )] =
74 ( QStringList() << QStringLiteral(
"GTiff" ) << tr(
"High Compression" )
75 << QStringLiteral(
"COMPRESS=DEFLATE PREDICTOR=2 ZLEVEL=9" ) );
76 sBuiltinProfiles[ QStringLiteral(
"z_gtiff_4jpeg" )] =
77 ( QStringList() << QStringLiteral(
"GTiff" ) << tr(
"JPEG Compression" )
78 << QStringLiteral(
"COMPRESS=JPEG JPEG_QUALITY=75" ) );
83 sBuiltinProfiles[ QStringLiteral(
"z__pyramids_gtiff_1big" )] =
84 ( QStringList() << QStringLiteral(
"_pyramids" ) << tr(
"No Compression" )
85 << QStringLiteral(
"COMPRESS_OVERVIEW=NONE BIGTIFF_OVERVIEW=IF_NEEDED" ) );
86 sBuiltinProfiles[ QStringLiteral(
"z__pyramids_gtiff_2medium" )] =
87 ( QStringList() << QStringLiteral(
"_pyramids" ) << tr(
"Low Compression" )
88 << QStringLiteral(
"COMPRESS_OVERVIEW=PACKBITS" ) );
89 sBuiltinProfiles[ QStringLiteral(
"z__pyramids_gtiff_3small" )] =
90 ( QStringList() << QStringLiteral(
"_pyramids" ) << tr(
"High Compression" )
91 << QStringLiteral(
"COMPRESS_OVERVIEW=DEFLATE PREDICTOR_OVERVIEW=2 ZLEVEL=9" ) );
92 sBuiltinProfiles[ QStringLiteral(
"z__pyramids_gtiff_4jpeg" )] =
93 ( QStringList() << QStringLiteral(
"_pyramids" ) << tr(
"JPEG Compression" )
94 << PYRAMID_JPEG_YCBCR_COMPRESSION );
97 connect( mProfileComboBox, &QComboBox::currentTextChanged,
98 this, &QgsRasterFormatSaveOptionsWidget::updateOptions );
99 connect( mOptionsTable, &QTableWidget::cellChanged,
this, &QgsRasterFormatSaveOptionsWidget::optionsTableChanged );
101 connect( mOptionsValidateButton, &QAbstractButton::clicked,
this, [ = ] {
validateOptions(); } );
104 mOptionsLineEdit->installEventFilter(
this );
107 setContextMenuPolicy( Qt::CustomContextMenu );
108 connect(
this, &QWidget::customContextMenuRequested,
this, [
this]( QPoint pos )
112 if ( mTableWidget->isVisible() )
113 text = tr(
"Use Simple Interface" );
115 text = tr(
"Use Table Interface" );
116 QAction *swapAction = menu.addAction( text );
117 connect( swapAction, &QAction::triggered,
this, [
this]() {swapOptionsUI( -1 ); } );
118 menu.exec( this->mapToGlobal( pos ) );
137 mProvider = provider;
144 const QList< QWidget * > widgets = this->findChildren<QWidget *>();
148 const auto constWidgets = widgets;
149 for ( QWidget *widget : constWidgets )
150 widget->setVisible(
false );
151 mOptionsWidget->setVisible(
true );
162 const auto constWidgets = widgets;
163 for ( QWidget *widget : constWidgets )
164 widget->setVisible(
true );
166 mProfileButtons->setVisible(
false );
176QString QgsRasterFormatSaveOptionsWidget::pseudoFormat()
const
178 return mPyramids ? QStringLiteral(
"_pyramids" ) : mFormat;
184 const QString format = pseudoFormat();
185 QStringList profileKeys = profiles();
186 QMapIterator<QString, QStringList> it( sBuiltinProfiles );
187 while ( it.hasNext() )
190 const QString profileKey = it.key();
191 if ( ! profileKeys.contains( profileKey ) && !it.value().isEmpty() )
194 if ( it.value()[0].isEmpty() || it.value()[0] == format )
196 profileKeys.insert( 0, profileKey );
200 std::sort( profileKeys.begin(), profileKeys.end() );
204 mProfileComboBox->blockSignals(
true );
205 mProfileComboBox->clear();
206 const auto constProfileKeys = profileKeys;
207 for (
const QString &profileKey : constProfileKeys )
209 QString profileName, profileOptions;
210 profileOptions = createOptions( profileKey );
211 if ( sBuiltinProfiles.contains( profileKey ) )
213 profileName = sBuiltinProfiles[ profileKey ][ 1 ];
214 if ( profileOptions.isEmpty() )
215 profileOptions = sBuiltinProfiles[ profileKey ][ 2 ];
219 profileName = profileKey;
221 mOptionsMap[ profileKey ] = profileOptions;
222 mProfileComboBox->addItem( profileName, profileKey );
226 mProfileComboBox->blockSignals(
false );
229 mProfileComboBox->setCurrentIndex( mProfileComboBox->findData( mySettings.
value(
230 mProvider +
"/driverOptions/" + format.toLower() +
"/defaultProfile",
235void QgsRasterFormatSaveOptionsWidget::updateOptions()
237 mBlockOptionUpdates++;
238 QString myOptions = mOptionsMap.value( currentProfileKey() );
239#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
240 QStringList myOptionsList = myOptions.trimmed().split(
' ', QString::SkipEmptyParts );
242 QStringList myOptionsList = myOptions.trimmed().split(
' ', Qt::SkipEmptyParts );
247 if ( mRasterLayer && mRasterLayer->
bandCount() != 3 &&
248 myOptions == PYRAMID_JPEG_YCBCR_COMPRESSION )
250 myOptions = PYRAMID_JPEG_COMPRESSION;
253 if ( mTableWidget->isVisible() )
255 mOptionsTable->setRowCount( 0 );
256 for (
int i = 0; i < myOptionsList.count(); i++ )
258 QStringList key_value = myOptionsList[i].split(
'=' );
259 if ( key_value.count() == 2 )
261 mOptionsTable->insertRow( i );
262 mOptionsTable->setItem( i, 0,
new QTableWidgetItem( key_value[0] ) );
263 mOptionsTable->setItem( i, 1,
new QTableWidgetItem( key_value[1] ) );
269 mOptionsLineEdit->setText( myOptions );
270 mOptionsLineEdit->setCursorPosition( 0 );
273 mBlockOptionUpdates--;
286 if ( mProvider == QLatin1String(
"gdal" ) && !mFormat.isEmpty() && ! mPyramids )
289 if ( message.isEmpty() )
290 message = tr(
"Cannot get create options for driver %1" ).arg( mFormat );
292 else if ( mProvider == QLatin1String(
"gdal" ) && mPyramids )
294 message = tr(
"For details on pyramids options please see the following pages" );
295 message += QLatin1String(
"\n\nhttps://gdal.org/programs/gdaladdo.html\n\nhttps://gdal.org/drivers/raster/gtiff.html" );
298 message = tr(
"No help available" );
302 dlg->setWindowTitle( tr(
"Create Options for %1" ).arg( mFormat ) );
303 QTextEdit *textEdit =
new QTextEdit( dlg );
304 textEdit->setReadOnly(
true );
306 textEdit->setText( message );
307 dlg->
layout()->addWidget( textEdit );
308 dlg->resize( 600, 400 );
318 const QStringList createOptions =
options();
321 QgsDebugMsgLevel( QStringLiteral(
"layer: [%1] file: [%2] format: [%3]" ).arg( mRasterLayer ? mRasterLayer->
id() :
"none", mRasterFileName, mFormat ), 2 );
325 bool tmpLayer =
false;
326 if ( !( mRasterLayer && rasterLayer->
dataProvider() ) && ! mRasterFileName.isNull() )
331 rasterLayer =
new QgsRasterLayer( mRasterFileName, QFileInfo( mRasterFileName ).baseName(), QStringLiteral(
"gdal" ),
options );
334 if ( mProvider == QLatin1String(
"gdal" ) && mPyramids )
338 QgsDebugMsgLevel( QStringLiteral(
"calling validate pyramids on layer's data provider" ), 2 );
343 message = tr(
"cannot validate pyramid options" );
346 else if ( !createOptions.isEmpty() && mProvider == QLatin1String(
"gdal" ) && !mFormat.isEmpty() )
350 QgsDebugMsgLevel( QStringLiteral(
"calling validate on layer's data provider" ), 2 );
360 else if ( ! createOptions.isEmpty() )
362 QMessageBox::information(
this, QString(), tr(
"Cannot validate creation options." ), QMessageBox::Close );
370 if ( message.isNull() )
373 QMessageBox::information(
this, QString(), tr(
"Valid" ), QMessageBox::Close );
377 QMessageBox::warning(
this, QString(), tr(
"Invalid %1:\n\n%2\n\nClick on help button to get valid creation options for this format." ).arg( mPyramids ? tr(
"pyramid creation option" ) : tr(
"creation option" ), message ), QMessageBox::Close );
387void QgsRasterFormatSaveOptionsWidget::optionsTableChanged()
389 if ( mBlockOptionUpdates )
392 QTableWidgetItem *key, *value;
394 for (
int i = 0; i < mOptionsTable->rowCount(); i++ )
396 key = mOptionsTable->item( i, 0 );
397 if ( ! key || key->text().isEmpty() )
399 value = mOptionsTable->item( i, 1 );
400 if ( ! value || value->text().isEmpty() )
402 options += key->text() +
'=' + value->text() +
' ';
405 mOptionsMap[ currentProfileKey()] =
options;
406 mOptionsLineEdit->setText(
options );
407 mOptionsLineEdit->setCursorPosition( 0 );
410void QgsRasterFormatSaveOptionsWidget::mOptionsLineEdit_editingFinished()
412 mOptionsMap[ currentProfileKey()] = mOptionsLineEdit->text().trimmed();
415void QgsRasterFormatSaveOptionsWidget::mProfileNewButton_clicked()
417 QString profileName = QInputDialog::getText(
this, QString(), tr(
"Profile name:" ) );
418 if ( ! profileName.isEmpty() )
420 profileName = profileName.trimmed();
421 mOptionsMap[ profileName ] = QString();
422 mProfileComboBox->addItem( profileName, profileName );
423 mProfileComboBox->setCurrentIndex( mProfileComboBox->count() - 1 );
427void QgsRasterFormatSaveOptionsWidget::mProfileDeleteButton_clicked()
429 const int index = mProfileComboBox->currentIndex();
430 const QString profileKey = currentProfileKey();
431 if ( index != -1 && ! sBuiltinProfiles.contains( profileKey ) )
433 mOptionsMap.remove( profileKey );
434 mProfileComboBox->removeItem( index );
438void QgsRasterFormatSaveOptionsWidget::mProfileResetButton_clicked()
440 const QString profileKey = currentProfileKey();
441 if ( sBuiltinProfiles.contains( profileKey ) )
443 mOptionsMap[ profileKey ] = sBuiltinProfiles[ profileKey ][ 2 ];
447 mOptionsMap[ profileKey ] = QString();
449 mOptionsLineEdit->setText( mOptionsMap.value( currentProfileKey() ) );
450 mOptionsLineEdit->setCursorPosition( 0 );
454void QgsRasterFormatSaveOptionsWidget::optionsTableEnableDeleteButton()
456 mOptionsDeleteButton->setEnabled( mOptionsTable->currentRow() >= 0 );
459void QgsRasterFormatSaveOptionsWidget::mOptionsAddButton_clicked()
461 mOptionsTable->insertRow( mOptionsTable->rowCount() );
463 const int newRow = mOptionsTable->rowCount() - 1;
464 QTableWidgetItem *item =
new QTableWidgetItem();
465 mOptionsTable->setItem( newRow, 0, item );
466 mOptionsTable->setCurrentItem( item );
469void QgsRasterFormatSaveOptionsWidget::mOptionsDeleteButton_clicked()
471 if ( mOptionsTable->currentRow() >= 0 )
473 mOptionsTable->removeRow( mOptionsTable->currentRow() );
475 QTableWidgetItem *item = mOptionsTable->item( mOptionsTable->currentRow(), 0 );
476 mOptionsTable->setCurrentItem( item );
477 optionsTableChanged();
481QString QgsRasterFormatSaveOptionsWidget::settingsKey( QString profileName )
const
483 if ( !profileName.isEmpty() )
484 profileName =
"/profile_" + profileName;
486 profileName =
"/profile_default" + profileName;
487 return mProvider +
"/driverOptions/" + pseudoFormat().toLower() + profileName +
"/create";
490QString QgsRasterFormatSaveOptionsWidget::currentProfileKey()
const
492 return mProfileComboBox->currentData().toString();
497#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
498 return mOptionsMap.value( currentProfileKey() ).trimmed().split(
' ', QString::SkipEmptyParts );
500 return mOptionsMap.value( currentProfileKey() ).trimmed().split(
' ', Qt::SkipEmptyParts );
504QString QgsRasterFormatSaveOptionsWidget::createOptions(
const QString &profileName )
const
507 return mySettings.
value( settingsKey( profileName ),
"" ).toString();
510void QgsRasterFormatSaveOptionsWidget::deleteCreateOptions(
const QString &profileName )
513 mySettings.
remove( settingsKey( profileName ) );
516void QgsRasterFormatSaveOptionsWidget::setCreateOptions()
519 QStringList myProfiles;
520 QMap< QString, QString >::const_iterator i = mOptionsMap.constBegin();
521 while ( i != mOptionsMap.constEnd() )
523 setCreateOptions( i.key(), i.value() );
524 myProfiles << i.key();
527 mySettings.
setValue( mProvider +
"/driverOptions/" + pseudoFormat().toLower() +
"/profiles",
529 mySettings.
setValue( mProvider +
"/driverOptions/" + pseudoFormat().toLower() +
"/defaultProfile",
530 currentProfileKey().trimmed() );
533void QgsRasterFormatSaveOptionsWidget::setCreateOptions(
const QString &profileName,
const QString &options )
539void QgsRasterFormatSaveOptionsWidget::setCreateOptions(
const QString &profileName,
const QStringList &list )
541 setCreateOptions( profileName, list.join( QLatin1Char(
' ' ) ) );
544QStringList QgsRasterFormatSaveOptionsWidget::profiles()
const
547 return mySettings.
value( mProvider +
"/driverOptions/" + pseudoFormat().toLower() +
"/profiles",
"" ).toStringList();
550void QgsRasterFormatSaveOptionsWidget::swapOptionsUI(
int newIndex )
555 bool lineEditMode = mOptionsLineEdit->isVisible();
556 mOptionsLineEdit->setVisible( ( newIndex == -1 && !lineEditMode ) || newIndex == 1 );
557 mTableWidget->setVisible( ( newIndex == -1 && lineEditMode ) || newIndex == 0 );
561void QgsRasterFormatSaveOptionsWidget::updateControls()
563 const bool valid = mProvider == QLatin1String(
"gdal" ) && !mFormat.isEmpty();
564 mOptionsValidateButton->setEnabled( valid );
565 mOptionsHelpButton->setEnabled( valid );
569bool QgsRasterFormatSaveOptionsWidget::eventFilter( QObject *obj, QEvent *event )
571 if ( event->type() == QEvent::ContextMenu )
573 QContextMenuEvent *contextEvent =
static_cast<QContextMenuEvent *
>( event );
574 QMenu *menu =
nullptr;
575 menu = mOptionsLineEdit->createStandardContextMenu();
576 menu->addSeparator();
577 QAction *action =
new QAction( tr(
"Use Table Interface" ), menu );
578 menu->addAction( action );
579 connect( action, &QAction::triggered,
this, [
this] { swapOptionsUI( 0 ); } );
580 menu->exec( contextEvent->globalPos() );
586 return QObject::eventFilter( obj, event );
592 mOptionsTable->horizontalHeader()->resizeSection( 0, mOptionsTable->width() - 115 );
598 mBlockOptionUpdates++;
599 mOptionsTable->clearContents();
601#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
602 const QStringList optionsList =
options.trimmed().split(
' ', QString::SkipEmptyParts );
604 const QStringList optionsList =
options.trimmed().split(
' ', Qt::SkipEmptyParts );
606 for (
const QString &opt : optionsList )
608 const int rowCount = mOptionsTable->rowCount();
609 mOptionsTable->insertRow( rowCount );
611 const QStringList values = opt.split(
'=' );
612 if ( values.count() == 2 )
614 QTableWidgetItem *nameItem =
new QTableWidgetItem( values.at( 0 ) );
615 mOptionsTable->setItem( rowCount, 0, nameItem );
616 QTableWidgetItem *valueItem =
new QTableWidgetItem( values.at( 1 ) );
617 mOptionsTable->setItem( rowCount, 1, valueItem );
623 mProfileComboBox->setCurrentIndex( 0 );
625 mOptionsMap[ currentProfileKey()] =
options.trimmed();
626 mOptionsLineEdit->setText(
options.trimmed() );
627 mOptionsLineEdit->setCursorPosition( 0 );
629 mBlockOptionUpdates--;
A generic dialog with layout and button box.
QVBoxLayout * layout()
Returns the central layout. Widgets added to it must have this dialog as parent.
static QString helpCreationOptionsFormat(const QString &format)
Gets creation options metadata for a given format.
static QString validateCreationOptionsFormat(const QStringList &createOptions, const QString &format)
Validates creation options for a given format, regardless of layer.
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
virtual QString validateCreationOptions(const QStringList &createOptions, const QString &format)
Validates creation options for a specific dataset and destination format.
virtual QString validatePyramidsConfigOptions(Qgis::RasterPyramidFormat pyramidsFormat, const QStringList &configOptions, const QString &fileFormat)
Validates pyramid creation options for a specific dataset and destination format.
Represents a raster layer.
int bandCount() const
Returns the number of bands in this layer.
QgsRasterDataProvider * dataProvider() override
Returns the source data provider.
This class is a composition of two QSettings instances:
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void remove(const QString &key, QgsSettings::Section section=QgsSettings::NoSection)
Removes the setting key and any sub-settings of key in a section.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
#define QgsDebugMsgLevel(str, level)
Setting options for loading raster layers.
bool skipCrsValidation
Controls whether the layer is allowed to have an invalid/unknown CRS.