QGIS API Documentation  2.99.0-Master (6c64c5a)
qgsrasterlayersaveasdialog.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsrasterlayersaveasdialog.cpp
3  ---------------------
4  begin : May 2012
5  copyright : (C) 2012 by Marco Hugentobler
6  email : marco dot hugentobler at sourcepole dot ch
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 #include "qgsapplication.h"
16 #include "qgslogger.h"
17 #include "qgscoordinatetransform.h"
18 #include "qgsrasterlayer.h"
20 #include "qgsrasterdataprovider.h"
22 #include "qgsrasterrenderer.h"
23 #include "qgsrastertransparency.h"
25 #include "qgssettings.h"
26 #include "qgsrasterfilewriter.h"
27 #include "cpl_string.h"
28 #include "qgsproject.h"
29 #include <gdal.h>
30 
31 #include <QFileDialog>
32 #include <QMessageBox>
33 
35  QgsRasterDataProvider *sourceProvider, const QgsRectangle &currentExtent,
36  const QgsCoordinateReferenceSystem &layerCrs, const QgsCoordinateReferenceSystem &currentCrs,
37  QWidget *parent, Qt::WindowFlags f )
38  : QDialog( parent, f )
39  , mRasterLayer( rasterLayer )
40  , mDataProvider( sourceProvider )
41  , mCurrentExtent( currentExtent )
42  , mLayerCrs( layerCrs )
43  , mCurrentCrs( currentCrs )
44  , mResolutionState( OriginalResolution )
45 {
46  setupUi( this );
47  connect( mRawModeRadioButton, &QRadioButton::toggled, this, &QgsRasterLayerSaveAsDialog::mRawModeRadioButton_toggled );
48  connect( mFormatComboBox, static_cast<void ( QComboBox::* )( const QString & )>( &QComboBox::currentIndexChanged ), this, &QgsRasterLayerSaveAsDialog::mFormatComboBox_currentIndexChanged );
49  connect( mResolutionRadioButton, &QRadioButton::toggled, this, &QgsRasterLayerSaveAsDialog::mResolutionRadioButton_toggled );
50  connect( mOriginalResolutionPushButton, &QPushButton::clicked, this, &QgsRasterLayerSaveAsDialog::mOriginalResolutionPushButton_clicked );
51  connect( mXResolutionLineEdit, &QLineEdit::textEdited, this, &QgsRasterLayerSaveAsDialog::mXResolutionLineEdit_textEdited );
52  connect( mYResolutionLineEdit, &QLineEdit::textEdited, this, &QgsRasterLayerSaveAsDialog::mYResolutionLineEdit_textEdited );
53  connect( mOriginalSizePushButton, &QPushButton::clicked, this, &QgsRasterLayerSaveAsDialog::mOriginalSizePushButton_clicked );
54  connect( mColumnsLineEdit, &QLineEdit::textEdited, this, &QgsRasterLayerSaveAsDialog::mColumnsLineEdit_textEdited );
55  connect( mRowsLineEdit, &QLineEdit::textEdited, this, &QgsRasterLayerSaveAsDialog::mRowsLineEdit_textEdited );
56  connect( mAddNoDataManuallyToolButton, &QPushButton::clicked, this, &QgsRasterLayerSaveAsDialog::mAddNoDataManuallyToolButton_clicked );
57  connect( mLoadTransparentNoDataToolButton, &QPushButton::clicked, this, &QgsRasterLayerSaveAsDialog::mLoadTransparentNoDataToolButton_clicked );
58  connect( mRemoveSelectedNoDataToolButton, &QPushButton::clicked, this, &QgsRasterLayerSaveAsDialog::mRemoveSelectedNoDataToolButton_clicked );
59  connect( mRemoveAllNoDataToolButton, &QPushButton::clicked, this, &QgsRasterLayerSaveAsDialog::mRemoveAllNoDataToolButton_clicked );
60  connect( mTileModeCheckBox, &QCheckBox::toggled, this, &QgsRasterLayerSaveAsDialog::mTileModeCheckBox_toggled );
61  connect( mPyramidsGroupBox, &QgsCollapsibleGroupBox::toggled, this, &QgsRasterLayerSaveAsDialog::mPyramidsGroupBox_toggled );
62  mAddNoDataManuallyToolButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/symbologyAdd.svg" ) ) );
63  mLoadTransparentNoDataToolButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionFileOpen.svg" ) ) );
64  mRemoveSelectedNoDataToolButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/symbologyRemove.svg" ) ) );
65  mRemoveAllNoDataToolButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionRemove.svg" ) ) );
66 
67  mNoDataTableWidget->setColumnCount( 2 );
68  mNoDataTableWidget->setHorizontalHeaderItem( 0, new QTableWidgetItem( tr( "From" ) ) );
69  mNoDataTableWidget->setHorizontalHeaderItem( 1, new QTableWidgetItem( tr( "To" ) ) );
70 
71  mRawModeRadioButton_toggled( true );
72 
73  setValidators();
74 
75  toggleResolutionSize();
76 
77  insertAvailableOutputFormats();
78 
79  //fill reasonable default values depending on the provider
80  if ( mDataProvider )
81  {
82  if ( mDataProvider->capabilities() & QgsRasterDataProvider::Size )
83  {
84  setOriginalResolution();
85  int xSize = mDataProvider->xSize();
86  int ySize = mDataProvider->ySize();
87  mMaximumSizeXLineEdit->setText( QString::number( xSize ) );
88  mMaximumSizeYLineEdit->setText( QString::number( ySize ) );
89  }
90  else //wms, sometimes wcs
91  {
92  mTileModeCheckBox->setChecked( true );
93  mMaximumSizeXLineEdit->setText( QString::number( 2000 ) );
94  mMaximumSizeYLineEdit->setText( QString::number( 2000 ) );
95  }
96 
97  // setup creation option widget
98  mCreateOptionsWidget->setProvider( mDataProvider->name() );
99  if ( mDataProvider->name() == QLatin1String( "gdal" ) )
100  {
101  mCreateOptionsWidget->setFormat( mFormatComboBox->currentData().toString() );
102  }
103  mCreateOptionsWidget->setRasterLayer( mRasterLayer );
104  mCreateOptionsWidget->update();
105  }
106 
107  // Only do pyramids if dealing directly with GDAL.
108  if ( mDataProvider && mDataProvider->capabilities() & QgsRasterDataProvider::BuildPyramids )
109  {
110  // setup pyramids option widget
111  // mPyramidsOptionsWidget->createOptionsWidget()->setType( QgsRasterFormatSaveOptionsWidget::ProfileLineEdit );
112  mPyramidsOptionsWidget->createOptionsWidget()->setRasterLayer( mRasterLayer );
113 
114  // TODO enable "use existing", has no effect for now, because using Create() in gdal provider
115  // if ( ! mDataProvider->hasPyramids() )
116  // mPyramidsButtonGroup->button( QgsRaster::PyramidsCopyExisting )->setEnabled( false );
117  mPyramidsUseExistingCheckBox->setEnabled( false );
118  mPyramidsUseExistingCheckBox->setVisible( false );
119 
120  populatePyramidsLevels();
121  connect( mPyramidsOptionsWidget, &QgsRasterPyramidsOptionsWidget::overviewListChanged,
122  this, &QgsRasterLayerSaveAsDialog::populatePyramidsLevels );
123  }
124  else
125  {
126  mPyramidsGroupBox->setEnabled( false );
127  }
128 
129  // restore checked state for most groupboxes (default is to restore collapsed state)
130  // create options and pyramids will be preset, if user has selected defaults in the gdal options dlg
131  mCreateOptionsGroupBox->setSaveCheckedState( true );
132  //mTilesGroupBox->setSaveCheckedState( true );
133  // don't restore nodata, it needs user input
134  // pyramids are not necessarily built every time
135 
136  mTilesGroupBox->hide();
137 
138  mCrsSelector->setLayerCrs( mLayerCrs );
139  //default to layer CRS - see https://issues.qgis.org/issues/14209 for discussion
140  mCrsSelector->setCrs( mLayerCrs );
141 
142  connect( mCrsSelector, &QgsProjectionSelectionWidget::crsChanged,
143  this, &QgsRasterLayerSaveAsDialog::crsChanged );
144 
145  QPushButton *okButton = mButtonBox->button( QDialogButtonBox::Ok );
146  if ( okButton )
147  {
148  okButton->setEnabled( false );
149  }
150 
151  connect( mButtonBox, &QDialogButtonBox::helpRequested, this, &QgsRasterLayerSaveAsDialog::showHelp );
152 
153  mExtentGroupBox->setOutputCrs( outputCrs() );
154  mExtentGroupBox->setOriginalExtent( mDataProvider->extent(), mLayerCrs );
155  mExtentGroupBox->setCurrentExtent( mCurrentExtent, mCurrentCrs );
156  mExtentGroupBox->setOutputExtentFromOriginal();
157  connect( mExtentGroupBox, &QgsExtentGroupBox::extentChanged, this, &QgsRasterLayerSaveAsDialog::extentChanged );
158 
159  recalcResolutionSize();
160 
161  QgsSettings settings;
162  restoreGeometry( settings.value( QStringLiteral( "Windows/RasterLayerSaveAs/geometry" ) ).toByteArray() );
163 
164  if ( mTileModeCheckBox->isChecked() )
165  {
166  mFilename->setStorageMode( QgsFileWidget::GetDirectory );
167  mFilename->setDialogTitle( tr( "Select output directory" ) );
168  }
169  else
170  {
171  mFilename->setStorageMode( QgsFileWidget::SaveFile );
172  mFilename->setDialogTitle( tr( "Save Layer as…" ) );
173  }
174  mFilename->setDefaultRoot( settings.value( QStringLiteral( "UI/lastRasterFileDir" ), QDir::homePath() ).toString() );
175  connect( mFilename, &QgsFileWidget::fileChanged, this, [ = ]( const QString & filePath )
176  {
177  QgsSettings settings;
178  QFileInfo tmplFileInfo( filePath );
179  settings.setValue( QStringLiteral( "UI/lastRasterFileDir" ), tmplFileInfo.absolutePath() );
180 
181  if ( mTileModeCheckBox->isChecked() )
182  {
183  QString fileName = filePath;
184  Q_FOREVER
185  {
186  // TODO: would not it be better to select .vrt file instead of directory?
187  //fileName = QFileDialog::getSaveFileName( this, tr( "Select output file" ), QString(), tr( "VRT" ) + " (*.vrt *.VRT)" );
188  if ( fileName.isEmpty() )
189  break; // canceled
190 
191  // Check if directory is empty
192  QDir dir( fileName );
193  QString baseName = QFileInfo( fileName ).baseName();
194  QStringList filters;
195  filters << QStringLiteral( "%1.*" ).arg( baseName );
196  QStringList files = dir.entryList( filters );
197  if ( files.isEmpty() )
198  break;
199 
200  if ( QMessageBox::warning( this, tr( "Warning" ),
201  tr( "The directory %1 contains files which will be overwritten: %2" ).arg( dir.absolutePath(), files.join( QStringLiteral( ", " ) ) ),
202  QMessageBox::Ok | QMessageBox::Cancel ) == QMessageBox::Ok )
203  break;
204 
205  fileName = QFileDialog::getExistingDirectory( this, tr( "Select output directory" ), tmplFileInfo.absolutePath() );
206  }
207  }
208 
209  QPushButton *okButton = mButtonBox->button( QDialogButtonBox::Ok );
210  if ( !okButton )
211  {
212  return;
213  }
214  okButton->setEnabled( tmplFileInfo.absoluteDir().exists() );
215  } );
216 }
217 
219 {
220  QgsSettings settings;
221  settings.setValue( QStringLiteral( "Windows/RasterLayerSaveAs/geometry" ), saveGeometry() );
222 }
223 
224 void QgsRasterLayerSaveAsDialog::insertAvailableOutputFormats()
225 {
226  GDALAllRegister();
227 
228  int nDrivers = GDALGetDriverCount();
229  QMap< int, QPair< QString, QString > > topPriorityDrivers;
230  QMap< QString, QString > lowPriorityDrivers;
231 
232  for ( int i = 0; i < nDrivers; ++i )
233  {
234  GDALDriverH driver = GDALGetDriver( i );
235  if ( driver )
236  {
237  char **driverMetadata = GDALGetMetadata( driver, nullptr );
238 
239  if ( CSLFetchBoolean( driverMetadata, GDAL_DCAP_CREATE, false ) && CSLFetchBoolean( driverMetadata, GDAL_DCAP_RASTER, false ) )
240  {
241  QString driverShortName = GDALGetDriverShortName( driver );
242  QString driverLongName = GDALGetDriverLongName( driver );
243  if ( driverShortName == QLatin1String( "MEM" ) )
244  {
245  // in memory rasters are not (yet) supported because the GDAL dataset handle
246  // would need to be passed directly to QgsRasterLayer (it is not possible to
247  // close it in raster calculator and reopen the dataset again in raster layer)
248  continue;
249  }
250  else if ( driverShortName == QLatin1String( "VRT" ) )
251  {
252  // skip GDAL vrt driver, since we handle that format manually
253  continue;
254  }
255  else if ( driverShortName == QStringLiteral( "GTiff" ) )
256  {
257  // always list geotiff first
258  topPriorityDrivers.insert( 1, qMakePair( driverLongName, driverShortName ) );
259  }
260  else if ( driverShortName == QStringLiteral( "GPKG" ) )
261  {
262  // and gpkg second
263  topPriorityDrivers.insert( 2, qMakePair( driverLongName, driverShortName ) );
264  }
265  else
266  {
267  lowPriorityDrivers.insert( driverLongName, driverShortName );
268  }
269  }
270  }
271  }
272 
273  // will be sorted by priority, so that geotiff and geopackage are listed first
274  for ( auto priorityDriversIt = topPriorityDrivers.constBegin(); priorityDriversIt != topPriorityDrivers.constEnd(); ++priorityDriversIt )
275  {
276  mFormatComboBox->addItem( priorityDriversIt.value().first, priorityDriversIt.value().second );
277  }
278  // will be sorted by driver name
279  for ( auto lowPriorityDriversIt = lowPriorityDrivers.constBegin(); lowPriorityDriversIt != lowPriorityDrivers.constEnd(); ++lowPriorityDriversIt )
280  {
281  mFormatComboBox->addItem( lowPriorityDriversIt.key(), lowPriorityDriversIt.value() );
282  }
283 
284 }
285 
286 void QgsRasterLayerSaveAsDialog::setValidators()
287 {
288  mXResolutionLineEdit->setValidator( new QDoubleValidator( this ) );
289  mYResolutionLineEdit->setValidator( new QDoubleValidator( this ) );
290  mColumnsLineEdit->setValidator( new QIntValidator( this ) );
291  mRowsLineEdit->setValidator( new QIntValidator( this ) );
292  mMaximumSizeXLineEdit->setValidator( new QIntValidator( this ) );
293  mMaximumSizeYLineEdit->setValidator( new QIntValidator( this ) );
294 }
295 
296 void QgsRasterLayerSaveAsDialog::mFormatComboBox_currentIndexChanged( const QString & )
297 {
298  //gdal-specific
299  if ( mDataProvider && mDataProvider->name() == QLatin1String( "gdal" ) )
300  {
301  mCreateOptionsWidget->setFormat( outputFormat() );
302  mCreateOptionsWidget->update();
303  }
304 
305  QStringList extensions = QgsRasterFileWriter::extensionsForFormat( outputFormat() );
306  QString filter;
307  if ( extensions.empty() )
308  filter = tr( "All files (*.*)" );
309  else
310  {
311  filter = QStringLiteral( "%1 (*.%2);;%3" ).arg( mFormatComboBox->currentText(),
312  extensions.join( QStringLiteral( " *." ) ),
313  tr( "All files (*.*)" ) );
314  }
315  mFilename->setFilter( filter );
316 }
317 
319 {
320  return mColumnsLineEdit->text().toInt();
321 }
322 
324 {
325  return mRowsLineEdit->text().toInt();
326 }
327 
329 {
330  return mXResolutionLineEdit->text().toDouble();
331 }
332 
334 {
335  return mYResolutionLineEdit->text().toDouble();
336 }
337 
339 {
340  return mMaximumSizeXLineEdit->text().toInt();
341 }
342 
344 {
345  return mMaximumSizeYLineEdit->text().toInt();
346 }
347 
349 {
350  return mTileModeCheckBox->isChecked();
351 }
352 
354 {
355  return mAddToCanvas->isChecked();
356 }
357 
359 {
360  QStringList extensions = QgsRasterFileWriter::extensionsForFormat( outputFormat() );
361  QString defaultExt;
362  if ( !extensions.empty() )
363  {
364  defaultExt = extensions.at( 0 );
365  }
366 
367  // ensure the user never omits the extension from the file name
368  QString fileName = mFilename->filePath();
369  QFileInfo fi( fileName );
370  if ( !fileName.isEmpty() && fi.suffix().isEmpty() )
371  {
372  fileName += '.' + defaultExt;
373  }
374 
375  return fileName;
376 }
377 
379 {
380  return mFormatComboBox->currentData().toString();
381 }
382 
384 {
385  return mCreateOptionsGroupBox->isChecked() ? mCreateOptionsWidget->options() : QStringList();
386 }
387 
389 {
390  return mExtentGroupBox->outputExtent();
391 }
392 
394 {
395  mFormatLabel->hide();
396  mFormatComboBox->hide();
397 }
398 
400 {
401  mSaveAsLabel->hide();
402  mFilename->hide();
403  QPushButton *okButton = mButtonBox->button( QDialogButtonBox::Ok );
404  if ( okButton )
405  {
406  okButton->setEnabled( true );
407  }
408 }
409 
410 void QgsRasterLayerSaveAsDialog::toggleResolutionSize()
411 {
412  bool hasResolution = mDataProvider && mDataProvider->capabilities() & QgsRasterDataProvider::Size;
413 
414  bool on = mResolutionRadioButton->isChecked();
415  mXResolutionLineEdit->setEnabled( on );
416  mYResolutionLineEdit->setEnabled( on );
417  mOriginalResolutionPushButton->setEnabled( on && hasResolution );
418  mColumnsLineEdit->setEnabled( !on );
419  mRowsLineEdit->setEnabled( !on );
420  mOriginalSizePushButton->setEnabled( !on && hasResolution );
421 }
422 
423 void QgsRasterLayerSaveAsDialog::setOriginalResolution()
424 {
425  double xRes, yRes;
426 
427  if ( mDataProvider->capabilities() & QgsRasterDataProvider::Size )
428  {
429  xRes = mDataProvider->extent().width() / mDataProvider->xSize();
430  yRes = mDataProvider->extent().height() / mDataProvider->ySize();
431  }
432  else
433  {
434  // Init to something if no original resolution is available
435  xRes = yRes = mDataProvider->extent().width() / 100;
436  }
437  setResolution( xRes, yRes, mLayerCrs );
438  mResolutionState = OriginalResolution;
439  recalcSize();
440 }
441 
442 void QgsRasterLayerSaveAsDialog::setResolution( double xRes, double yRes, const QgsCoordinateReferenceSystem &srcCrs )
443 {
444  if ( srcCrs != outputCrs() )
445  {
446  // We reproject pixel rectangle from center of selected extent, of course, it gives
447  // bigger xRes,yRes than reprojected edges (envelope), it may also be that
448  // close to margins are higher resolutions (even very, too high)
449  // TODO: consider more precise resolution calculation
450 
451  QgsPointXY center = outputRectangle().center();
454 
455  QgsRectangle srcExtent( srsCenter.x() - xRes / 2, srsCenter.y() - yRes / 2, srsCenter.x() + xRes / 2, srsCenter.y() + yRes / 2 );
456 
457  QgsRectangle extent = ct.transform( srcExtent );
458  xRes = extent.width();
459  yRes = extent.height();
460  }
461  mXResolutionLineEdit->setText( QString::number( xRes ) );
462  mYResolutionLineEdit->setText( QString::number( yRes ) );
463 }
464 
465 void QgsRasterLayerSaveAsDialog::recalcSize()
466 {
467  QgsRectangle extent = outputRectangle();
468  int xSize = xResolution() != 0 ? static_cast<int>( std::round( extent.width() / xResolution() ) ) : 0;
469  int ySize = yResolution() != 0 ? static_cast<int>( std::round( extent.height() / yResolution() ) ) : 0;
470  mColumnsLineEdit->setText( QString::number( xSize ) );
471  mRowsLineEdit->setText( QString::number( ySize ) );
472  updateResolutionStateMsg();
473 }
474 
475 void QgsRasterLayerSaveAsDialog::setOriginalSize()
476 {
477  mColumnsLineEdit->setText( QString::number( mDataProvider->xSize() ) );
478  mRowsLineEdit->setText( QString::number( mDataProvider->ySize() ) );
479  recalcResolution();
480 }
481 
482 void QgsRasterLayerSaveAsDialog::recalcResolution()
483 {
484  QgsRectangle extent = outputRectangle();
485  double xRes = nColumns() != 0 ? extent.width() / nColumns() : 0;
486  double yRes = nRows() != 0 ? extent.height() / nRows() : 0;
487  mXResolutionLineEdit->setText( QString::number( xRes ) );
488  mYResolutionLineEdit->setText( QString::number( yRes ) );
489  updateResolutionStateMsg();
490 }
491 
492 void QgsRasterLayerSaveAsDialog::recalcResolutionSize()
493 {
494  if ( mResolutionRadioButton->isChecked() )
495  {
496  recalcSize();
497  }
498  else
499  {
500  mResolutionState = UserResolution;
501  recalcResolution();
502  }
503 }
504 
505 void QgsRasterLayerSaveAsDialog::updateResolutionStateMsg()
506 {
507  QString msg;
508  switch ( mResolutionState )
509  {
510  case OriginalResolution:
511  msg = tr( "layer" );
512  break;
513  case UserResolution:
514  msg = tr( "user defined" );
515  break;
516  default:
517  break;
518  }
519  msg = tr( "Resolution (current: %1)" ).arg( msg );
520  mResolutionGroupBox->setTitle( msg );
521 }
522 
523 void QgsRasterLayerSaveAsDialog::extentChanged()
524 {
525  // Whenever extent changes with fixed size, original resolution is lost
526  if ( mSizeRadioButton->isChecked() )
527  {
528  mResolutionState = UserResolution;
529  }
530  recalcResolutionSize();
531 }
532 
533 void QgsRasterLayerSaveAsDialog::crsChanged()
534 {
535  if ( outputCrs() != mPreviousCrs )
536  {
537  mExtentGroupBox->setOutputCrs( outputCrs() );
538 
539  // Reset resolution
540  if ( mResolutionRadioButton->isChecked() )
541  {
542  if ( mResolutionState == OriginalResolution )
543  {
544  setOriginalResolution();
545  }
546  else
547  {
548  // reset from present resolution and present crs
549  setResolution( xResolution(), yResolution(), mPreviousCrs );
550  }
551  }
552  else
553  {
554  // Size does not change, we just recalc resolution from new extent
555  recalcResolution();
556  }
557  }
558  mPreviousCrs = outputCrs();
559 }
560 
562 {
563  return mCrsSelector->crs();
564 }
565 
567 {
568  if ( mRenderedModeRadioButton->isChecked() ) return RenderedImageMode;
569  return RawDataMode;
570 }
571 
572 void QgsRasterLayerSaveAsDialog::mRawModeRadioButton_toggled( bool checked )
573 {
574  mNoDataGroupBox->setEnabled( checked && mDataProvider->bandCount() == 1 );
575 }
576 
577 void QgsRasterLayerSaveAsDialog::mAddNoDataManuallyToolButton_clicked()
578 {
579  addNoDataRow( std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN() );
580 }
581 
582 void QgsRasterLayerSaveAsDialog::mLoadTransparentNoDataToolButton_clicked()
583 {
584  if ( !mRasterLayer->renderer() ) return;
585  const QgsRasterTransparency *rasterTransparency = mRasterLayer->renderer()->rasterTransparency();
586  if ( !rasterTransparency ) return;
587 
588  Q_FOREACH ( const QgsRasterTransparency::TransparentSingleValuePixel &transparencyPixel, rasterTransparency->transparentSingleValuePixelList() )
589  {
590  if ( transparencyPixel.percentTransparent == 100 )
591  {
592  addNoDataRow( transparencyPixel.min, transparencyPixel.max );
593  if ( transparencyPixel.min != transparencyPixel.max )
594  {
595  setNoDataToEdited( mNoDataTableWidget->rowCount() - 1 );
596  }
597  }
598  }
599 }
600 
601 void QgsRasterLayerSaveAsDialog::mRemoveSelectedNoDataToolButton_clicked()
602 {
603  mNoDataTableWidget->removeRow( mNoDataTableWidget->currentRow() );
604 }
605 
606 void QgsRasterLayerSaveAsDialog::mRemoveAllNoDataToolButton_clicked()
607 {
608  while ( mNoDataTableWidget->rowCount() > 0 )
609  {
610  mNoDataTableWidget->removeRow( 0 );
611  }
612 }
613 
614 void QgsRasterLayerSaveAsDialog::addNoDataRow( double min, double max )
615 {
616  mNoDataTableWidget->insertRow( mNoDataTableWidget->rowCount() );
617  for ( int i = 0; i < 2; i++ )
618  {
619  double value = i == 0 ? min : max;
620  QLineEdit *lineEdit = new QLineEdit();
621  lineEdit->setFrame( false );
622  lineEdit->setContentsMargins( 1, 1, 1, 1 );
623  QString valueString;
624  switch ( mRasterLayer->dataProvider()->sourceDataType( 1 ) )
625  {
626  case Qgis::Float32:
627  case Qgis::Float64:
628  lineEdit->setValidator( new QDoubleValidator( nullptr ) );
629  if ( !std::isnan( value ) )
630  {
631  valueString = QgsRasterBlock::printValue( value );
632  }
633  break;
634  default:
635  lineEdit->setValidator( new QIntValidator( nullptr ) );
636  if ( !std::isnan( value ) )
637  {
638  valueString = QString::number( static_cast<int>( value ) );
639  }
640  break;
641  }
642  lineEdit->setText( valueString );
643  mNoDataTableWidget->setCellWidget( mNoDataTableWidget->rowCount() - 1, i, lineEdit );
644 
645  adjustNoDataCellWidth( mNoDataTableWidget->rowCount() - 1, i );
646 
647  connect( lineEdit, &QLineEdit::textEdited, this, &QgsRasterLayerSaveAsDialog::noDataCellTextEdited );
648  }
649  mNoDataTableWidget->resizeColumnsToContents();
650  mNoDataTableWidget->resizeRowsToContents();
651 }
652 
653 void QgsRasterLayerSaveAsDialog::noDataCellTextEdited( const QString &text )
654 {
655  Q_UNUSED( text );
656 
657  QLineEdit *lineEdit = dynamic_cast<QLineEdit *>( sender() );
658  if ( !lineEdit ) return;
659  int row = -1;
660  int column = -1;
661  for ( int r = 0; r < mNoDataTableWidget->rowCount(); r++ )
662  {
663  for ( int c = 0; c < mNoDataTableWidget->columnCount(); c++ )
664  {
665  if ( mNoDataTableWidget->cellWidget( r, c ) == sender() )
666  {
667  row = r;
668  column = c;
669  break;
670  }
671  }
672  if ( row != -1 ) break;
673  }
674  QgsDebugMsg( QString( "row = %1 column =%2" ).arg( row ).arg( column ) );
675 
676  if ( column == 0 )
677  {
678  QLineEdit *toLineEdit = dynamic_cast<QLineEdit *>( mNoDataTableWidget->cellWidget( row, 1 ) );
679  if ( !toLineEdit ) return;
680  bool toChanged = mNoDataToEdited.value( row );
681  QgsDebugMsg( QString( "toChanged = %1" ).arg( toChanged ) );
682  if ( !toChanged )
683  {
684  toLineEdit->setText( lineEdit->text() );
685  }
686  }
687  else if ( column == 1 )
688  {
689  setNoDataToEdited( row );
690  }
691 }
692 
693 void QgsRasterLayerSaveAsDialog::mTileModeCheckBox_toggled( bool toggled )
694 {
695  if ( toggled )
696  {
697  // enable pyramids
698 
699  // Disabled (Radim), auto enabling of pyramids was making impression that
700  // we (programmers) know better what you (user) want to do,
701  // certainly auto expaning was bad experience
702 
703  //if ( ! mPyramidsGroupBox->isChecked() )
704  // mPyramidsGroupBox->setChecked( true );
705 
706  // Auto expanding mPyramidsGroupBox is bad - it auto crolls content of dialog
707  //if ( mPyramidsGroupBox->isCollapsed() )
708  // mPyramidsGroupBox->setCollapsed( false );
709  //mPyramidsOptionsWidget->checkAllLevels( true );
710 
711  // Show / hide tile options
712  mTilesGroupBox->show();
713  mFilename->setStorageMode( QgsFileWidget::GetDirectory );
714  mFilename->setDialogTitle( tr( "Select output directory" ) );
715  }
716  else
717  {
718  mTilesGroupBox->hide();
719  mFilename->setStorageMode( QgsFileWidget::SaveFile );
720  mFilename->setDialogTitle( tr( "Save Layer as…" ) );
721  }
722 }
723 
724 void QgsRasterLayerSaveAsDialog::mPyramidsGroupBox_toggled( bool toggled )
725 {
726  Q_UNUSED( toggled );
727  populatePyramidsLevels();
728 }
729 
730 void QgsRasterLayerSaveAsDialog::populatePyramidsLevels()
731 {
732  QString text;
733 
734  if ( mPyramidsGroupBox->isChecked() )
735  {
736  QList<QgsRasterPyramid> myPyramidList;
737  // if use existing, get pyramids from actual layer
738  // but that's not available yet
739  if ( mPyramidsUseExistingCheckBox->isChecked() )
740  {
741  myPyramidList = mDataProvider->buildPyramidList();
742  }
743  else
744  {
745  if ( ! mPyramidsOptionsWidget->overviewList().isEmpty() )
746  myPyramidList = mDataProvider->buildPyramidList( mPyramidsOptionsWidget->overviewList() );
747  }
748  QList<QgsRasterPyramid>::iterator myRasterPyramidIterator;
749  for ( myRasterPyramidIterator = myPyramidList.begin();
750  myRasterPyramidIterator != myPyramidList.end();
751  ++myRasterPyramidIterator )
752  {
753  if ( ! mPyramidsUseExistingCheckBox->isChecked() || myRasterPyramidIterator->exists )
754  {
755  text += QString::number( myRasterPyramidIterator->xDim ) + QStringLiteral( "x" ) +
756  QString::number( myRasterPyramidIterator->yDim ) + ' ';
757  }
758  }
759  }
760 
761  mPyramidResolutionsLineEdit->setText( text.trimmed() );
762 }
763 
764 void QgsRasterLayerSaveAsDialog::setNoDataToEdited( int row )
765 {
766  if ( row >= mNoDataToEdited.size() )
767  {
768  mNoDataToEdited.resize( row + 1 );
769  }
770  mNoDataToEdited[row] = true;
771 }
772 
773 double QgsRasterLayerSaveAsDialog::noDataCellValue( int row, int column ) const
774 {
775  QLineEdit *lineEdit = dynamic_cast<QLineEdit *>( mNoDataTableWidget->cellWidget( row, column ) );
776  if ( !lineEdit || lineEdit->text().isEmpty() )
777  {
778  std::numeric_limits<double>::quiet_NaN();
779  }
780  return lineEdit->text().toDouble();
781 }
782 
783 void QgsRasterLayerSaveAsDialog::adjustNoDataCellWidth( int row, int column )
784 {
785  QLineEdit *lineEdit = dynamic_cast<QLineEdit *>( mNoDataTableWidget->cellWidget( row, column ) );
786  if ( !lineEdit ) return;
787 
788  int width = std::max( lineEdit->fontMetrics().width( lineEdit->text() ) + 10, 100 );
789  width = std::max( width, mNoDataTableWidget->columnWidth( column ) );
790 
791  lineEdit->setFixedWidth( width );
792 }
793 
795 {
796  QgsRasterRangeList noDataList;
797  if ( ! mNoDataGroupBox->isChecked() )
798  return noDataList;
799 
800  int rows = mNoDataTableWidget->rowCount();
801  noDataList.reserve( rows );
802  for ( int r = 0; r < rows; r++ )
803  {
804  QgsRasterRange noData( noDataCellValue( r, 0 ), noDataCellValue( r, 1 ) );
805  noDataList.append( noData );
806 
807  }
808  return noDataList;
809 }
810 
812 {
813  return mPyramidsGroupBox->isChecked() ? mPyramidsOptionsWidget->overviewList() : QList<int>();
814 }
815 
817 {
818  if ( ! mPyramidsGroupBox->isChecked() )
820  else if ( mPyramidsUseExistingCheckBox->isChecked() )
822  else
824 }
825 
826 bool QgsRasterLayerSaveAsDialog::validate() const
827 {
828  if ( mCreateOptionsGroupBox->isChecked() )
829  {
830  QString message = mCreateOptionsWidget->validateOptions( true, false );
831  if ( !message.isNull() )
832  return false;
833  }
834  if ( mPyramidsGroupBox->isChecked() )
835  {
836  QString message = mPyramidsOptionsWidget->createOptionsWidget()->validateOptions( true, false );
837  if ( !message.isNull() )
838  return false;
839  }
840  return true;
841 }
842 
843 void QgsRasterLayerSaveAsDialog::showHelp()
844 {
845  QgsHelp::openHelp( QStringLiteral( "managing_data_source/create_layers.html#save-layer-from-an-existing-file" ) );
846 }
virtual int bandCount() const =0
Get number of bands.
A rectangle specified with double values.
Definition: qgsrectangle.h:39
static QString printValue(double value)
Print double value with all necessary significant digits.
QgsRaster::RasterBuildPyramids buildPyramidsFlag() const
void fileChanged(const QString &)
emitted as soon as the current file or directory is changed
void extentChanged(const QgsRectangle &r)
Emitted when the widget&#39;s extent is changed.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:57
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
This class provides qgis with the ability to render raster datasets onto the mapcanvas.
Select a single file.
Definition: qgsfilewidget.h:66
double y
Definition: qgspointxy.h:48
A class to represent a 2D point.
Definition: qgspointxy.h:43
virtual int ySize() const
static QStringList extensionsForFormat(const QString &format)
Returns a list of known file extensions for the given GDAL driver format.
Qgis::DataType sourceDataType(int bandNo) const override=0
Returns source data type for the band specified by number, source data type may be shorter than dataT...
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
Raster values range container.
QgsCoordinateReferenceSystem outputCrs()
QgsRasterRenderer * renderer() const
Thirty two bit floating point (float)
Definition: qgis.h:99
const QgsRasterTransparency * rasterTransparency() const
virtual QString name() const =0
Return a provider name.
Sixty four bit floating point (double)
Definition: qgis.h:100
void saveGeometry(QWidget *widget, const QString &keyName)
Save the wigget geometry into settings.
bool restoreGeometry(QWidget *widget, const QString &keyName)
Restore the wigget geometry from settings.
QgsRasterDataProvider * dataProvider() override
void setValue(const QString &key, const QVariant &value, const QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
virtual QList< QgsRasterPyramid > buildPyramidList(QList< int > overviewList=QList< int >())
Accessor for the raster layers pyramid list.
virtual int capabilities() const
Returns a bitmask containing the supported capabilities.
QgsRectangle extent() const override=0
Returns the extent of the layer.
double width() const
Returns the width of the rectangle.
Definition: qgsrectangle.h:142
double x
Definition: qgspointxy.h:47
QList< QgsRasterTransparency::TransparentSingleValuePixel > transparentSingleValuePixelList() const
Accessor for transparentSingleValuePixelList.
QgsRasterLayerSaveAsDialog(QgsRasterLayer *rasterLayer, QgsRasterDataProvider *sourceProvider, const QgsRectangle &currentExtent, const QgsCoordinateReferenceSystem &layerCrs, const QgsCoordinateReferenceSystem &currentCrs, QWidget *parent=nullptr, Qt::WindowFlags f=nullptr)
Constructor for QgsRasterLayerSaveAsDialog.
Transform from destination to source CRS.
QList< QgsRasterRange > QgsRasterRangeList
void crsChanged(const QgsCoordinateReferenceSystem &)
Emitted when the selected CRS is changed.
QgsPointXY transform(const QgsPointXY &point, TransformDirection direction=ForwardTransform) const
Transform the point from the source CRS to the destination CRS.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), const Section section=NoSection) const
Returns the value for setting key.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:383
This class represents a coordinate reference system (CRS).
Select multiple files.
Definition: qgsfilewidget.h:68
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
Definition: qgshelp.cpp:35
Class for doing transforms between two map coordinate systems.
RasterBuildPyramids
Definition: qgsraster.h:74
Defines the list of pixel values to be considered as transparent or semi transparent when rendering r...
QgsPointXY center() const
Returns the center point of the rectangle.
Definition: qgsrectangle.h:170
virtual int xSize() const
Get raster size.
double height() const
Returns the height of the rectangle.
Definition: qgsrectangle.h:149
Base class for raster data providers.