27#include <QContextMenuEvent>
29#include <QInputDialog>
35#include "moc_qgsrasterformatsaveoptionswidget.cpp"
37using namespace Qt::StringLiterals;
39QMap<QString, QStringList> QgsRasterFormatSaveOptionsWidget::sBuiltinProfiles;
41static const QString PYRAMID_JPEG_YCBCR_COMPRESSION( u
"JPEG_QUALITY_OVERVIEW=75 COMPRESS_OVERVIEW=JPEG PHOTOMETRIC_OVERVIEW=YCBCR INTERLEAVE_OVERVIEW=PIXEL"_s );
42static const QString PYRAMID_JPEG_COMPRESSION( u
"JPEG_QUALITY_OVERVIEW=75 COMPRESS_OVERVIEW=JPEG INTERLEAVE_OVERVIEW=PIXEL"_s );
47 , mProvider( provider )
52 mOptionsTable->setMinimumSize( 200, mOptionsTable->verticalHeader()->defaultSectionSize() * 4 + mOptionsTable->horizontalHeader()->height() + 2 );
54 connect( mProfileNewButton, &QPushButton::clicked,
this, &QgsRasterFormatSaveOptionsWidget::mProfileNewButton_clicked );
55 connect( mProfileDeleteButton, &QPushButton::clicked,
this, &QgsRasterFormatSaveOptionsWidget::mProfileDeleteButton_clicked );
56 connect( mProfileResetButton, &QPushButton::clicked,
this, &QgsRasterFormatSaveOptionsWidget::mProfileResetButton_clicked );
57 connect( mOptionsAddButton, &QPushButton::clicked,
this, &QgsRasterFormatSaveOptionsWidget::mOptionsAddButton_clicked );
58 connect( mOptionsDeleteButton, &QPushButton::clicked,
this, &QgsRasterFormatSaveOptionsWidget::mOptionsDeleteButton_clicked );
59 connect( mOptionsLineEdit, &QLineEdit::editingFinished,
this, &QgsRasterFormatSaveOptionsWidget::mOptionsLineEdit_editingFinished );
63 if ( sBuiltinProfiles.isEmpty() )
66 sBuiltinProfiles[u
"z_adefault"_s] = ( QStringList() << QString() << tr(
"Default" ) << QString() );
71 sBuiltinProfiles[u
"z_gtiff_1big"_s] = ( QStringList() << u
"GTiff"_s << tr(
"No Compression" ) << u
"COMPRESS=NONE BIGTIFF=IF_NEEDED"_s );
72 sBuiltinProfiles[u
"z_gtiff_2medium"_s] = ( QStringList() << u
"GTiff"_s << tr(
"Low Compression" ) << u
"COMPRESS=PACKBITS"_s );
73 sBuiltinProfiles[u
"z_gtiff_3small"_s] = ( QStringList() << u
"GTiff"_s << tr(
"High Compression" ) << u
"COMPRESS=DEFLATE PREDICTOR=2 ZLEVEL=9"_s );
74 sBuiltinProfiles[u
"z_gtiff_4jpeg"_s] = ( QStringList() << u
"GTiff"_s << tr(
"JPEG Compression" ) << u
"COMPRESS=JPEG JPEG_QUALITY=75"_s );
79 sBuiltinProfiles[u
"z__pyramids_gtiff_1big"_s] = ( QStringList() << u
"_pyramids"_s << tr(
"No Compression" ) << u
"COMPRESS_OVERVIEW=NONE BIGTIFF_OVERVIEW=IF_NEEDED"_s );
80 sBuiltinProfiles[u
"z__pyramids_gtiff_2medium"_s] = ( QStringList() << u
"_pyramids"_s << tr(
"Low Compression" ) << u
"COMPRESS_OVERVIEW=PACKBITS"_s );
81 sBuiltinProfiles[u
"z__pyramids_gtiff_3small"_s] = ( QStringList() << u
"_pyramids"_s << tr(
"High Compression" ) << u
"COMPRESS_OVERVIEW=DEFLATE PREDICTOR_OVERVIEW=2 ZLEVEL=9"_s );
82 sBuiltinProfiles[u
"z__pyramids_gtiff_4jpeg"_s] = ( QStringList() << u
"_pyramids"_s << tr(
"JPEG Compression" ) << PYRAMID_JPEG_YCBCR_COMPRESSION );
85 connect( mProfileComboBox, &QComboBox::currentTextChanged,
this, &QgsRasterFormatSaveOptionsWidget::updateOptions );
86 connect( mOptionsTable, &QTableWidget::cellChanged,
this, &QgsRasterFormatSaveOptionsWidget::optionsTableChanged );
88 connect( mOptionsValidateButton, &QAbstractButton::clicked,
this, [
this] {
validateOptions(); } );
91 mOptionsLineEdit->installEventFilter(
this );
94 setContextMenuPolicy( Qt::CustomContextMenu );
95 connect(
this, &QWidget::customContextMenuRequested,
this, [
this]( QPoint pos ) {
98 if ( mTableWidget->isVisible() )
99 text = tr(
"Use Simple Interface" );
101 text = tr(
"Use Table Interface" );
102 QAction *swapAction = menu.addAction( text );
103 connect( swapAction, &QAction::triggered,
this, [
this]() { swapOptionsUI( -1 ); } );
104 menu.exec( this->mapToGlobal( pos ) );
123 mProvider = provider;
130 const QList<QWidget *> widgets = this->findChildren<QWidget *>();
134 const auto constWidgets = widgets;
135 for ( QWidget *widget : constWidgets )
136 widget->setVisible(
false );
137 mOptionsWidget->setVisible(
true );
148 const auto constWidgets = widgets;
149 for ( QWidget *widget : constWidgets )
150 widget->setVisible(
true );
152 mProfileButtons->setVisible(
false );
162QString QgsRasterFormatSaveOptionsWidget::pseudoFormat()
const
164 return mPyramids ? u
"_pyramids"_s : mFormat;
170 const QString format = pseudoFormat();
171 QStringList profileKeys = profiles();
172 QMapIterator<QString, QStringList> it( sBuiltinProfiles );
173 while ( it.hasNext() )
176 const QString profileKey = it.key();
177 if ( !profileKeys.contains( profileKey ) && !it.value().isEmpty() )
180 if ( it.value()[0].isEmpty() || it.value()[0] == format )
182 profileKeys.insert( 0, profileKey );
186 std::sort( profileKeys.begin(), profileKeys.end() );
190 mProfileComboBox->blockSignals(
true );
191 mProfileComboBox->clear();
192 const auto constProfileKeys = profileKeys;
193 for (
const QString &profileKey : constProfileKeys )
195 QString profileName, profileOptions;
196 profileOptions = creationOptions( profileKey );
197 if ( sBuiltinProfiles.contains( profileKey ) )
199 profileName = sBuiltinProfiles[profileKey][1];
200 if ( profileOptions.isEmpty() )
201 profileOptions = sBuiltinProfiles[profileKey][2];
205 profileName = profileKey;
207 mOptionsMap[profileKey] = profileOptions;
208 mProfileComboBox->addItem( profileName, profileKey );
212 mProfileComboBox->blockSignals(
false );
215 mProfileComboBox->setCurrentIndex( mProfileComboBox->findData( mySettings.
value(
216 mProvider +
"/driverOptions/" + format.toLower() +
"/defaultProfile",
222void QgsRasterFormatSaveOptionsWidget::updateOptions()
224 mBlockOptionUpdates++;
225 QString myOptions = mOptionsMap.value( currentProfileKey() );
226 QStringList myOptionsList = myOptions.trimmed().split(
' ', Qt::SkipEmptyParts );
230 if ( mRasterLayer && mRasterLayer->
bandCount() != 3 && myOptions == PYRAMID_JPEG_YCBCR_COMPRESSION )
232 myOptions = PYRAMID_JPEG_COMPRESSION;
235 if ( mTableWidget->isVisible() )
237 mOptionsTable->setRowCount( 0 );
238 for (
int i = 0; i < myOptionsList.count(); i++ )
240 QStringList key_value = myOptionsList[i].split(
'=' );
241 if ( key_value.count() == 2 )
243 mOptionsTable->insertRow( i );
244 mOptionsTable->setItem( i, 0,
new QTableWidgetItem( key_value[0] ) );
245 mOptionsTable->setItem( i, 1,
new QTableWidgetItem( key_value[1] ) );
251 mOptionsLineEdit->setText( myOptions );
252 mOptionsLineEdit->setCursorPosition( 0 );
255 mBlockOptionUpdates--;
261 setCreationOptions();
268 if ( mProvider ==
"gdal"_L1 && !mFormat.isEmpty() && !mPyramids )
271 if ( message.isEmpty() )
272 message = tr(
"Cannot get create options for driver %1" ).arg( mFormat );
274 else if ( mProvider ==
"gdal"_L1 && mPyramids )
276 message = tr(
"For details on pyramids options please see the following pages" );
277 message +=
"\n\nhttps://gdal.org/programs/gdaladdo.html\n\nhttps://gdal.org/drivers/raster/gtiff.html"_L1;
280 message = tr(
"No help available" );
284 dlg->setWindowTitle( tr(
"Create Options for %1" ).arg( mFormat ) );
285 QTextEdit *textEdit =
new QTextEdit( dlg );
286 textEdit->setReadOnly(
true );
288 textEdit->setText( message );
289 dlg->
layout()->addWidget( textEdit );
290 dlg->resize( 600, 400 );
300 const QStringList creationOptions =
options();
303 QgsDebugMsgLevel( u
"layer: [%1] file: [%2] format: [%3]"_s.arg( mRasterLayer ? mRasterLayer->id() :
"none", mRasterFileName, mFormat ), 2 );
307 bool tmpLayer =
false;
308 if ( !( mRasterLayer && rasterLayer->
dataProvider() ) && !mRasterFileName.isNull() )
312 options.skipCrsValidation =
true;
313 rasterLayer =
new QgsRasterLayer( mRasterFileName, QFileInfo( mRasterFileName ).baseName(), u
"gdal"_s,
options );
316 if ( mProvider ==
"gdal"_L1 && mPyramids )
320 QgsDebugMsgLevel( u
"calling validate pyramids on layer's data provider"_s, 2 );
325 message = tr(
"cannot validate pyramid options" );
328 else if ( !creationOptions.isEmpty() && mProvider ==
"gdal"_L1 && !mFormat.isEmpty() )
341 else if ( !creationOptions.isEmpty() )
343 QMessageBox::information(
this, QString(), tr(
"Cannot validate creation options." ), QMessageBox::Close );
351 if ( message.isNull() )
354 QMessageBox::information(
this, QString(), tr(
"Valid" ), QMessageBox::Close );
358 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 );
368void QgsRasterFormatSaveOptionsWidget::optionsTableChanged()
370 if ( mBlockOptionUpdates )
373 QTableWidgetItem *key, *value;
375 for (
int i = 0; i < mOptionsTable->rowCount(); i++ )
377 key = mOptionsTable->item( i, 0 );
378 if ( !key || key->text().isEmpty() )
380 value = mOptionsTable->item( i, 1 );
381 if ( !value || value->text().isEmpty() )
383 options += key->text() +
'=' + value->text() +
' ';
386 mOptionsMap[currentProfileKey()] =
options;
387 mOptionsLineEdit->setText(
options );
388 mOptionsLineEdit->setCursorPosition( 0 );
391void QgsRasterFormatSaveOptionsWidget::mOptionsLineEdit_editingFinished()
393 mOptionsMap[currentProfileKey()] = mOptionsLineEdit->text().trimmed();
396void QgsRasterFormatSaveOptionsWidget::mProfileNewButton_clicked()
398 QString profileName = QInputDialog::getText(
this, QString(), tr(
"Profile name:" ) );
399 if ( !profileName.isEmpty() )
401 profileName = profileName.trimmed();
402 mOptionsMap[profileName] = QString();
403 mProfileComboBox->addItem( profileName, profileName );
404 mProfileComboBox->setCurrentIndex( mProfileComboBox->count() - 1 );
408void QgsRasterFormatSaveOptionsWidget::mProfileDeleteButton_clicked()
410 const int index = mProfileComboBox->currentIndex();
411 const QString profileKey = currentProfileKey();
412 if ( index != -1 && !sBuiltinProfiles.contains( profileKey ) )
414 mOptionsMap.remove( profileKey );
415 mProfileComboBox->removeItem( index );
419void QgsRasterFormatSaveOptionsWidget::mProfileResetButton_clicked()
421 const QString profileKey = currentProfileKey();
422 if ( sBuiltinProfiles.contains( profileKey ) )
424 mOptionsMap[profileKey] = sBuiltinProfiles[profileKey][2];
428 mOptionsMap[profileKey] = QString();
430 mOptionsLineEdit->setText( mOptionsMap.value( currentProfileKey() ) );
431 mOptionsLineEdit->setCursorPosition( 0 );
435void QgsRasterFormatSaveOptionsWidget::optionsTableEnableDeleteButton()
437 mOptionsDeleteButton->setEnabled( mOptionsTable->currentRow() >= 0 );
440void QgsRasterFormatSaveOptionsWidget::mOptionsAddButton_clicked()
442 mOptionsTable->insertRow( mOptionsTable->rowCount() );
444 const int newRow = mOptionsTable->rowCount() - 1;
445 QTableWidgetItem *item =
new QTableWidgetItem();
446 mOptionsTable->setItem( newRow, 0, item );
447 mOptionsTable->setCurrentItem( item );
450void QgsRasterFormatSaveOptionsWidget::mOptionsDeleteButton_clicked()
452 if ( mOptionsTable->currentRow() >= 0 )
454 mOptionsTable->removeRow( mOptionsTable->currentRow() );
456 QTableWidgetItem *item = mOptionsTable->item( mOptionsTable->currentRow(), 0 );
457 mOptionsTable->setCurrentItem( item );
458 optionsTableChanged();
462QString QgsRasterFormatSaveOptionsWidget::settingsKey( QString profileName )
const
464 if ( !profileName.isEmpty() )
465 profileName =
"/profile_" + profileName;
467 profileName =
"/profile_default" + profileName;
468 return mProvider +
"/driverOptions/" + pseudoFormat().toLower() + profileName +
"/create";
471QString QgsRasterFormatSaveOptionsWidget::currentProfileKey()
const
473 return mProfileComboBox->currentData().toString();
478 return mOptionsMap.value( currentProfileKey() ).trimmed().split(
' ', Qt::SkipEmptyParts );
481QString QgsRasterFormatSaveOptionsWidget::creationOptions(
const QString &profileName )
const
484 return mySettings.
value( settingsKey( profileName ),
"" ).toString();
487void QgsRasterFormatSaveOptionsWidget::deleteCreationOptions(
const QString &profileName )
489 QgsSettings mySettings;
490 mySettings.
remove( settingsKey( profileName ) );
493void QgsRasterFormatSaveOptionsWidget::setCreationOptions()
495 QgsSettings mySettings;
496 QStringList myProfiles;
497 QMap<QString, QString>::const_iterator i = mOptionsMap.constBegin();
498 while ( i != mOptionsMap.constEnd() )
500 setCreationOptions( i.key(), i.value() );
501 myProfiles << i.key();
504 mySettings.
setValue( mProvider +
"/driverOptions/" + pseudoFormat().toLower() +
"/profiles", myProfiles );
505 mySettings.
setValue( mProvider +
"/driverOptions/" + pseudoFormat().toLower() +
"/defaultProfile", currentProfileKey().trimmed() );
508void QgsRasterFormatSaveOptionsWidget::setCreationOptions(
const QString &profileName,
const QString &options )
510 QgsSettings mySettings;
514void QgsRasterFormatSaveOptionsWidget::setCreationOptions(
const QString &profileName,
const QStringList &options )
516 setCreationOptions( profileName,
options.join(
' '_L1 ) );
519QStringList QgsRasterFormatSaveOptionsWidget::profiles()
const
521 const QgsSettings mySettings;
522 return mySettings.
value( mProvider +
"/driverOptions/" + pseudoFormat().toLower() +
"/profiles",
"" ).toStringList();
525void QgsRasterFormatSaveOptionsWidget::swapOptionsUI(
int newIndex )
530 bool lineEditMode = mOptionsLineEdit->isVisible();
531 mOptionsLineEdit->setVisible( ( newIndex == -1 && !lineEditMode ) || newIndex == 1 );
532 mTableWidget->setVisible( ( newIndex == -1 && lineEditMode ) || newIndex == 0 );
536void QgsRasterFormatSaveOptionsWidget::updateControls()
538 const bool valid = mProvider ==
"gdal"_L1 && !mFormat.isEmpty();
539 mOptionsValidateButton->setEnabled( valid );
540 mOptionsHelpButton->setEnabled( valid );
544bool QgsRasterFormatSaveOptionsWidget::eventFilter( QObject *obj, QEvent *event )
546 if ( event->type() == QEvent::ContextMenu )
548 QContextMenuEvent *contextEvent =
static_cast<QContextMenuEvent *
>( event );
549 QMenu *menu =
nullptr;
550 menu = mOptionsLineEdit->createStandardContextMenu();
551 menu->addSeparator();
552 QAction *action =
new QAction( tr(
"Use Table Interface" ), menu );
553 menu->addAction( action );
554 connect( action, &QAction::triggered,
this, [
this] { swapOptionsUI( 0 ); } );
555 menu->exec( contextEvent->globalPos() );
560 return QObject::eventFilter( obj, event );
567 mOptionsTable->horizontalHeader()->resizeSection( 0, mOptionsTable->width() - 115 );
573 mBlockOptionUpdates++;
574 mOptionsTable->clearContents();
576 const QStringList optionsList =
options.trimmed().split(
' ', Qt::SkipEmptyParts );
577 for (
const QString &opt : optionsList )
579 const int rowCount = mOptionsTable->rowCount();
580 mOptionsTable->insertRow( rowCount );
582 const QStringList values = opt.split(
'=' );
583 if ( values.count() == 2 )
585 QTableWidgetItem *nameItem =
new QTableWidgetItem( values.at( 0 ) );
586 mOptionsTable->setItem( rowCount, 0, nameItem );
587 QTableWidgetItem *valueItem =
new QTableWidgetItem( values.at( 1 ) );
588 mOptionsTable->setItem( rowCount, 1, valueItem );
594 mProfileComboBox->setCurrentIndex( 0 );
596 mOptionsMap[currentProfileKey()] =
options.trimmed();
597 mOptionsLineEdit->setText(
options.trimmed() );
598 mOptionsLineEdit->setCursorPosition( 0 );
600 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 &creationOptions, const QString &format)
Validates creation options for a given format, regardless of layer.
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.
Stores settings for use within QGIS.
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.