27 #include <QInputDialog> 
   28 #include <QMessageBox> 
   30 #include <QMouseEvent> 
   34 QMap< QString, QStringList > QgsRasterFormatSaveOptionsWidget::sBuiltinProfiles;
 
   36 static const QString PYRAMID_JPEG_YCBCR_COMPRESSION( QStringLiteral( 
"JPEG_QUALITY_OVERVIEW=75 COMPRESS_OVERVIEW=JPEG PHOTOMETRIC_OVERVIEW=YCBCR INTERLEAVE_OVERVIEW=PIXEL" ) );
 
   37 static const QString PYRAMID_JPEG_COMPRESSION( QStringLiteral( 
"JPEG_QUALITY_OVERVIEW=75 COMPRESS_OVERVIEW=JPEG INTERLEAVE_OVERVIEW=PIXEL" ) );
 
   43   , mProvider( provider )
 
   46   connect( mProfileNewButton, &QPushButton::clicked, 
this, &QgsRasterFormatSaveOptionsWidget::mProfileNewButton_clicked );
 
   47   connect( mProfileDeleteButton, &QPushButton::clicked, 
this, &QgsRasterFormatSaveOptionsWidget::mProfileDeleteButton_clicked );
 
   48   connect( mProfileResetButton, &QPushButton::clicked, 
this, &QgsRasterFormatSaveOptionsWidget::mProfileResetButton_clicked );
 
   49   connect( mOptionsAddButton, &QPushButton::clicked, 
this, &QgsRasterFormatSaveOptionsWidget::mOptionsAddButton_clicked );
 
   50   connect( mOptionsDeleteButton, &QPushButton::clicked, 
this, &QgsRasterFormatSaveOptionsWidget::mOptionsDeleteButton_clicked );
 
   51   connect( mOptionsLineEdit, &QLineEdit::editingFinished, 
this, &QgsRasterFormatSaveOptionsWidget::mOptionsLineEdit_editingFinished );
 
   55   if ( sBuiltinProfiles.isEmpty() )
 
   58     sBuiltinProfiles[ QStringLiteral( 
"z_adefault" )] = ( QStringList() << QString() << tr( 
"Default" ) << QString() );
 
   63     sBuiltinProfiles[ QStringLiteral( 
"z_gtiff_1big" )] =
 
   64       ( QStringList() << QStringLiteral( 
"GTiff" ) << tr( 
"No Compression" )
 
   65         << QStringLiteral( 
"COMPRESS=NONE BIGTIFF=IF_NEEDED" ) );
 
   66     sBuiltinProfiles[ QStringLiteral( 
"z_gtiff_2medium" )] =
 
   67       ( QStringList() << QStringLiteral( 
"GTiff" ) << tr( 
"Low Compression" )
 
   68         << QStringLiteral( 
"COMPRESS=PACKBITS" ) );
 
   69     sBuiltinProfiles[ QStringLiteral( 
"z_gtiff_3small" )] =
 
   70       ( QStringList() << QStringLiteral( 
"GTiff" ) << tr( 
"High Compression" )
 
   71         << QStringLiteral( 
"COMPRESS=DEFLATE PREDICTOR=2 ZLEVEL=9" ) );
 
   72     sBuiltinProfiles[ QStringLiteral( 
"z_gtiff_4jpeg" )] =
 
   73       ( QStringList() << QStringLiteral( 
"GTiff" ) << tr( 
"JPEG Compression" )
 
   74         << QStringLiteral( 
"COMPRESS=JPEG JPEG_QUALITY=75" ) );
 
   79     sBuiltinProfiles[ QStringLiteral( 
"z__pyramids_gtiff_1big" )] =
 
   80       ( QStringList() << QStringLiteral( 
"_pyramids" ) << tr( 
"No Compression" )
 
   81         << QStringLiteral( 
"COMPRESS_OVERVIEW=NONE BIGTIFF_OVERVIEW=IF_NEEDED" ) );
 
   82     sBuiltinProfiles[ QStringLiteral( 
"z__pyramids_gtiff_2medium" )] =
 
   83       ( QStringList() << QStringLiteral( 
"_pyramids" ) << tr( 
"Low Compression" )
 
   84         << QStringLiteral( 
"COMPRESS_OVERVIEW=PACKBITS" ) );
 
   85     sBuiltinProfiles[ QStringLiteral( 
"z__pyramids_gtiff_3small" )] =
 
   86       ( QStringList() << QStringLiteral( 
"_pyramids" ) << tr( 
"High Compression" )
 
   87         << QStringLiteral( 
"COMPRESS_OVERVIEW=DEFLATE PREDICTOR_OVERVIEW=2 ZLEVEL=9" ) ); 
 
   88     sBuiltinProfiles[ QStringLiteral( 
"z__pyramids_gtiff_4jpeg" )] =
 
   89       ( QStringList() << QStringLiteral( 
"_pyramids" ) << tr( 
"JPEG Compression" )
 
   90         << PYRAMID_JPEG_YCBCR_COMPRESSION );
 
   93   connect( mProfileComboBox, &QComboBox::currentTextChanged,
 
   94            this, &QgsRasterFormatSaveOptionsWidget::updateOptions );
 
   95   connect( mOptionsTable, &QTableWidget::cellChanged, 
this, &QgsRasterFormatSaveOptionsWidget::optionsTableChanged );
 
   97   connect( mOptionsValidateButton, &QAbstractButton::clicked, 
this, [ = ] { 
validateOptions(); } );
 
  101   mOptionsLineEdit->installEventFilter( 
this );
 
  102   mOptionsStackedWidget->installEventFilter( 
this );
 
  119   mProvider = provider;
 
  126   QList< QWidget * > widgets = this->findChildren<QWidget *>();
 
  130     const auto constWidgets = widgets;
 
  131     for ( QWidget *widget : constWidgets )
 
  132       widget->setVisible( 
false );
 
  133     mOptionsStackedWidget->setVisible( 
true );
 
  134     const auto children { mOptionsStackedWidget->findChildren<QWidget *>() };
 
  135     for ( QWidget *widget : children )
 
  136       widget->setVisible( 
true );
 
  147     const auto constWidgets = widgets;
 
  148     for ( QWidget *widget : constWidgets )
 
  149       widget->setVisible( 
true );
 
  151       mProfileButtons->setVisible( 
false );
 
  159 QString QgsRasterFormatSaveOptionsWidget::pseudoFormat()
 const 
  161   return mPyramids ? QStringLiteral( 
"_pyramids" ) : mFormat;
 
  167   QString format = pseudoFormat();
 
  168   QStringList profileKeys = profiles();
 
  169   QMapIterator<QString, QStringList> it( sBuiltinProfiles );
 
  170   while ( it.hasNext() )
 
  173     QString profileKey = it.key();
 
  174     if ( ! profileKeys.contains( profileKey ) && !it.value().isEmpty() )
 
  177       if ( it.value()[0].isEmpty() ||  it.value()[0] == format )
 
  179         profileKeys.insert( 0, profileKey );
 
  183   std::sort( profileKeys.begin(), profileKeys.end() );
 
  187   mProfileComboBox->blockSignals( 
true );
 
  188   mProfileComboBox->clear();
 
  189   const auto constProfileKeys = profileKeys;
 
  190   for ( 
const QString &profileKey : constProfileKeys )
 
  192     QString profileName, profileOptions;
 
  193     profileOptions = createOptions( profileKey );
 
  194     if ( sBuiltinProfiles.contains( profileKey ) )
 
  196       profileName = sBuiltinProfiles[ profileKey ][ 1 ];
 
  197       if ( profileOptions.isEmpty() )
 
  198         profileOptions = sBuiltinProfiles[ profileKey ][ 2 ];
 
  202       profileName = profileKey;
 
  204     mOptionsMap[ profileKey ] = profileOptions;
 
  205     mProfileComboBox->addItem( profileName, profileKey );
 
  209   mProfileComboBox->blockSignals( 
false );
 
  212   mProfileComboBox->setCurrentIndex( mProfileComboBox->findData( mySettings.
value(
 
  213                                        mProvider + 
"/driverOptions/" + format.toLower() + 
"/defaultProfile",
 
  218 void QgsRasterFormatSaveOptionsWidget::updateOptions()
 
  220   mBlockOptionUpdates++;
 
  221   QString myOptions = mOptionsMap.value( currentProfileKey() );
 
  222   QStringList myOptionsList = myOptions.trimmed().split( 
' ', QString::SkipEmptyParts );
 
  226   if ( mRasterLayer && mRasterLayer->
bandCount() != 3 &&
 
  227        myOptions == PYRAMID_JPEG_YCBCR_COMPRESSION )
 
  229     myOptions = PYRAMID_JPEG_COMPRESSION;
 
  232   if ( mOptionsStackedWidget->currentIndex() == 0 )
 
  234     mOptionsTable->setRowCount( 0 );
 
  235     for ( 
int i = 0; i < myOptionsList.count(); i++ )
 
  237       QStringList key_value = myOptionsList[i].split( 
'=' );
 
  238       if ( key_value.count() == 2 )
 
  240         mOptionsTable->insertRow( i );
 
  241         mOptionsTable->setItem( i, 0, 
new QTableWidgetItem( key_value[0] ) );
 
  242         mOptionsTable->setItem( i, 1, 
new QTableWidgetItem( key_value[1] ) );
 
  248     mOptionsLineEdit->setText( myOptions );
 
  249     mOptionsLineEdit->setCursorPosition( 0 );
 
  252   mBlockOptionUpdates--;
 
  265   if ( mProvider == QLatin1String( 
"gdal" ) && !mFormat.isEmpty() && ! mPyramids )
 
  268     if ( message.isEmpty() )
 
  269       message = tr( 
"Cannot get create options for driver %1" ).arg( mFormat );
 
  271   else if ( mProvider == QLatin1String( 
"gdal" ) && mPyramids )
 
  273     message = tr( 
"For details on pyramids options please see the following pages" );
 
  274     message += QLatin1String( 
"\n\nhttps://gdal.org/programs/gdaladdo.html\n\nhttps://gdal.org/drivers/raster/gtiff.html" );
 
  277     message = tr( 
"No help available" );
 
  281   dlg->setWindowTitle( tr( 
"Create Options for %1" ).arg( mFormat ) );
 
  282   QTextEdit *textEdit = 
new QTextEdit( dlg );
 
  283   textEdit->setReadOnly( 
true );
 
  285   textEdit->setText( message );
 
  286   dlg->
layout()->addWidget( textEdit );
 
  287   dlg->resize( 600, 400 );
 
  297   QStringList createOptions = 
options();
 
  300   QgsDebugMsg( QStringLiteral( 
"layer: [%1] file: [%2] format: [%3]" ).arg( mRasterLayer ? mRasterLayer->
id() : 
"none", mRasterFileName, mFormat ) );
 
  304   bool tmpLayer = 
false;
 
  305   if ( !( mRasterLayer && rasterLayer->
dataProvider() ) && ! mRasterFileName.isNull() )
 
  309     options.skipCrsValidation = 
true;
 
  310     rasterLayer = 
new QgsRasterLayer( mRasterFileName, QFileInfo( mRasterFileName ).baseName(), QStringLiteral( 
"gdal" ), 
options );
 
  313   if ( mProvider == QLatin1String( 
"gdal" ) && mPyramids )
 
  317       QgsDebugMsg( QStringLiteral( 
"calling validate pyramids on layer's data provider" ) );
 
  322       message = tr( 
"cannot validate pyramid options" );
 
  325   else if ( !createOptions.isEmpty() && mProvider == QLatin1String( 
"gdal" ) && !mFormat.isEmpty() )
 
  329       QgsDebugMsg( QStringLiteral( 
"calling validate on layer's data provider" ) );
 
  339   else if ( ! createOptions.isEmpty() )
 
  341     QMessageBox::information( 
this, QString(), tr( 
"Cannot validate creation options." ), QMessageBox::Close );
 
  349     if ( message.isNull() )
 
  352         QMessageBox::information( 
this, QString(), tr( 
"Valid" ), QMessageBox::Close );
 
  356       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 );
 
  366 void QgsRasterFormatSaveOptionsWidget::optionsTableChanged()
 
  368   if ( mBlockOptionUpdates )
 
  371   QTableWidgetItem *key, *value;
 
  373   for ( 
int i = 0; i < mOptionsTable->rowCount(); i++ )
 
  375     key = mOptionsTable->item( i, 0 );
 
  376     if ( ! key  || key->text().isEmpty() )
 
  378     value = mOptionsTable->item( i, 1 );
 
  379     if ( ! value  || value->text().isEmpty() )
 
  381     options += key->text() + 
'=' + value->text() + 
' ';
 
  384   mOptionsMap[ currentProfileKey()] = 
options;
 
  385   mOptionsLineEdit->setText( 
options );
 
  386   mOptionsLineEdit->setCursorPosition( 0 );
 
  389 void QgsRasterFormatSaveOptionsWidget::mOptionsLineEdit_editingFinished()
 
  391   mOptionsMap[ currentProfileKey()] = mOptionsLineEdit->text().trimmed();
 
  394 void QgsRasterFormatSaveOptionsWidget::mProfileNewButton_clicked()
 
  396   QString profileName = QInputDialog::getText( 
this, QString(), tr( 
"Profile name:" ) );
 
  397   if ( ! profileName.isEmpty() )
 
  399     profileName = profileName.trimmed();
 
  400     mOptionsMap[ profileName ] = QString();
 
  401     mProfileComboBox->addItem( profileName, profileName );
 
  402     mProfileComboBox->setCurrentIndex( mProfileComboBox->count() - 1 );
 
  406 void QgsRasterFormatSaveOptionsWidget::mProfileDeleteButton_clicked()
 
  408   int index = mProfileComboBox->currentIndex();
 
  409   QString profileKey = currentProfileKey();
 
  410   if ( index != -1 && ! sBuiltinProfiles.contains( profileKey ) )
 
  412     mOptionsMap.remove( profileKey );
 
  413     mProfileComboBox->removeItem( index );
 
  417 void QgsRasterFormatSaveOptionsWidget::mProfileResetButton_clicked()
 
  419   QString profileKey = currentProfileKey();
 
  420   if ( sBuiltinProfiles.contains( profileKey ) )
 
  422     mOptionsMap[ profileKey ] = sBuiltinProfiles[ profileKey ][ 2 ];
 
  426     mOptionsMap[ profileKey ] = QString();
 
  428   mOptionsLineEdit->setText( mOptionsMap.value( currentProfileKey() ) );
 
  429   mOptionsLineEdit->setCursorPosition( 0 );
 
  433 void QgsRasterFormatSaveOptionsWidget::optionsTableEnableDeleteButton()
 
  435   mOptionsDeleteButton->setEnabled( mOptionsTable->currentRow() >= 0 );
 
  438 void QgsRasterFormatSaveOptionsWidget::mOptionsAddButton_clicked()
 
  440   mOptionsTable->insertRow( mOptionsTable->rowCount() );
 
  442   int newRow = mOptionsTable->rowCount() - 1;
 
  443   QTableWidgetItem *item = 
new QTableWidgetItem();
 
  444   mOptionsTable->setItem( newRow, 0, item );
 
  445   mOptionsTable->setCurrentItem( item );
 
  448 void QgsRasterFormatSaveOptionsWidget::mOptionsDeleteButton_clicked()
 
  450   if ( mOptionsTable->currentRow() >= 0 )
 
  452     mOptionsTable->removeRow( mOptionsTable->currentRow() );
 
  454     QTableWidgetItem *item = mOptionsTable->item( mOptionsTable->currentRow(), 0 );
 
  455     mOptionsTable->setCurrentItem( item );
 
  456     optionsTableChanged();
 
  460 QString QgsRasterFormatSaveOptionsWidget::settingsKey( QString profileName )
 const 
  462   if ( !profileName.isEmpty() )
 
  463     profileName = 
"/profile_" + profileName;
 
  465     profileName = 
"/profile_default" + profileName;
 
  466   return mProvider + 
"/driverOptions/" + pseudoFormat().toLower() + profileName + 
"/create";
 
  469 QString QgsRasterFormatSaveOptionsWidget::currentProfileKey()
 const 
  471   return mProfileComboBox->currentData().toString();
 
  476   return mOptionsMap.value( currentProfileKey() ).trimmed().split( 
' ', QString::SkipEmptyParts );
 
  479 QString QgsRasterFormatSaveOptionsWidget::createOptions( 
const QString &profileName )
 const 
  482   return mySettings.
value( settingsKey( profileName ), 
"" ).toString();
 
  485 void QgsRasterFormatSaveOptionsWidget::deleteCreateOptions( 
const QString &profileName )
 
  488   mySettings.
remove( settingsKey( profileName ) );
 
  491 void QgsRasterFormatSaveOptionsWidget::setCreateOptions()
 
  494   QStringList myProfiles;
 
  495   QMap< QString, QString >::const_iterator i = mOptionsMap.constBegin();
 
  496   while ( i != mOptionsMap.constEnd() )
 
  498     setCreateOptions( i.key(), i.value() );
 
  499     myProfiles << i.key();
 
  502   mySettings.
setValue( mProvider + 
"/driverOptions/" + pseudoFormat().toLower() + 
"/profiles",
 
  504   mySettings.
setValue( mProvider + 
"/driverOptions/" + pseudoFormat().toLower() + 
"/defaultProfile",
 
  505                        currentProfileKey().trimmed() );
 
  508 void QgsRasterFormatSaveOptionsWidget::setCreateOptions( 
const QString &profileName, 
const QString &options )
 
  514 void QgsRasterFormatSaveOptionsWidget::setCreateOptions( 
const QString &profileName, 
const QStringList &list )
 
  516   setCreateOptions( profileName, list.join( QLatin1Char( 
' ' ) ) );
 
  519 QStringList QgsRasterFormatSaveOptionsWidget::profiles()
 const 
  522   return mySettings.
value( mProvider + 
"/driverOptions/" + pseudoFormat().toLower() + 
"/profiles", 
"" ).toStringList();
 
  525 void QgsRasterFormatSaveOptionsWidget::swapOptionsUI( 
int newIndex )
 
  529   if ( newIndex == -1 )
 
  531     oldIndex = mOptionsStackedWidget->currentIndex();
 
  532     newIndex = ( oldIndex + 1 ) % 2;
 
  536     oldIndex = ( newIndex + 1 ) % 2;
 
  540   mOptionsStackedWidget->setCurrentIndex( newIndex );
 
  541   mOptionsStackedWidget->widget( newIndex )->setSizePolicy(
 
  542     QSizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred ) );
 
  543   mOptionsStackedWidget->widget( oldIndex )->setSizePolicy(
 
  544     QSizePolicy( QSizePolicy::Ignored, QSizePolicy::Ignored ) );
 
  545   layout()->activate();
 
  550 void QgsRasterFormatSaveOptionsWidget::updateControls()
 
  552   bool valid = mProvider == QLatin1String( 
"gdal" ) && !mFormat.isEmpty();
 
  553   mOptionsValidateButton->setEnabled( valid );
 
  554   mOptionsHelpButton->setEnabled( valid );
 
  558 bool QgsRasterFormatSaveOptionsWidget::eventFilter( QObject *obj, QEvent *event )
 
  560   if ( event->type() == QEvent::MouseButtonPress )
 
  562     QMouseEvent *mouseEvent = 
static_cast<QMouseEvent *
>( event );
 
  563     if ( mouseEvent && ( mouseEvent->button() == Qt::RightButton ) )
 
  565       QMenu *menu = 
nullptr;
 
  567       if ( mOptionsStackedWidget->currentIndex() == 0 )
 
  568         text = tr( 
"Use simple interface" );
 
  570         text = tr( 
"Use table interface" );
 
  571       if ( obj->objectName() == QLatin1String( 
"mOptionsLineEdit" ) )
 
  573         menu = mOptionsLineEdit->createStandardContextMenu();
 
  574         menu->addSeparator();
 
  577         menu = 
new QMenu( 
this );
 
  578       QAction *action = 
new QAction( text, menu );
 
  579       menu->addAction( action );
 
  580       connect( action, &QAction::triggered, 
this, &QgsRasterFormatSaveOptionsWidget::swapOptionsUI );
 
  581       menu->exec( mouseEvent->globalPos() );
 
  587   return QObject::eventFilter( obj, event );
 
  593   mOptionsTable->horizontalHeader()->resizeSection( 0, mOptionsTable->width() - 115 );
 
  599   mBlockOptionUpdates++;
 
  600   mOptionsTable->clearContents();
 
  602   const QStringList optionsList = 
options.trimmed().split( 
' ', QString::SkipEmptyParts );
 
  603   for ( 
const QString &opt : optionsList )
 
  605     int rowCount = mOptionsTable->rowCount();
 
  606     mOptionsTable->insertRow( rowCount );
 
  608     const QStringList values = opt.split( 
'=' );
 
  609     if ( values.count() == 2 )
 
  611       QTableWidgetItem *nameItem = 
new QTableWidgetItem( values.at( 0 ) );
 
  612       mOptionsTable->setItem( rowCount, 0, nameItem );
 
  613       QTableWidgetItem *valueItem = 
new QTableWidgetItem( values.at( 1 ) );
 
  614       mOptionsTable->setItem( rowCount, 1, valueItem );
 
  620   mProfileComboBox->setCurrentIndex( 0 );
 
  622   mOptionsMap[ currentProfileKey()] = 
options.trimmed();
 
  623   mOptionsLineEdit->setText( 
options.trimmed() );
 
  624   mOptionsLineEdit->setCursorPosition( 0 );
 
  626   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(QgsRaster::RasterPyramidsFormat 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.
Setting options for loading raster layers.