QGIS API Documentation 4.3.0-Master (1930e81c918)
Loading...
Searching...
No Matches
qgsvectorlayersaveasdialog.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsvectorlayersaveasdialog.h
3 Dialog to select destination, type and crs for ogr layers
4 -------------------
5 begin : Mon Mar 22 2010
6 copyright : (C) 2010 by Juergen E. Fischer
7 email : jef at norbit dot de
8 ***************************************************************************/
9
10/***************************************************************************
11 * *
12 * This program is free software; you can redistribute it and/or modify *
13 * it under the terms of the GNU General Public License as published by *
14 * the Free Software Foundation; either version 2 of the License, or *
15 * (at your option) any later version. *
16 * *
17 ***************************************************************************/
19
20#include <gdal.h>
21#include <limits>
22#include <optional>
23
25#include "qgsdatums.h"
28#include "qgsgui.h"
29#include "qgshelp.h"
30#include "qgsiconutils.h"
31#include "qgslogger.h"
32#include "qgsmapcanvas.h"
33#include "qgsmaplayerutils.h"
34#include "qgsproviderregistry.h"
36#include "qgssettings.h"
38
39#include <QFileDialog>
40#include <QMessageBox>
41#include <QRegularExpression>
42#include <QSpinBox>
43#include <QString>
44#include <QTextCodec>
45
46#include "moc_qgsvectorlayersaveasdialog.cpp"
47
48using namespace Qt::StringLiterals;
49
50QgsVectorLayerSaveAsDialog::QgsVectorLayerSaveAsDialog( long srsid, QWidget *parent, Qt::WindowFlags fl )
51 : QDialog( parent, fl )
52 , mSelectedCrs( QgsCoordinateReferenceSystem::fromSrsId( srsid ) )
53 , mActionOnExistingFile( QgsVectorFileWriter::CreateOrOverwriteFile )
54{
55 setup();
56}
57
59 : QDialog( parent, fl )
60 , mLayer( layer )
61 , mActionOnExistingFile( QgsVectorFileWriter::CreateOrOverwriteFile )
62 , mOptions( options )
63{
64 if ( layer )
65 {
66 mSelectedCrs = layer->crs();
67 mLayerExtent = layer->extent();
68 }
69 setup();
70
71 if ( layer )
72 {
73 mDefaultOutputLayerNameFromInputLayerName = QgsMapLayerUtils::launderLayerName( layer->name() );
74 leLayername->setDefaultValue( mDefaultOutputLayerNameFromInputLayerName );
75 leLayername->setClearMode( QgsFilterLineEdit::ClearToDefault );
76 if ( leLayername->isEnabled() )
77 leLayername->setText( mDefaultOutputLayerNameFromInputLayerName );
78 }
79
80 if ( !( mOptions & Option::Symbology ) )
81 {
82 mSymbologyExportLabel->hide();
83 mSymbologyExportComboBox->hide();
84 mScaleLabel->hide();
85 mScaleWidget->hide();
86 }
87
88 if ( !( mOptions & Option::DestinationCrs ) )
89 {
90 mCrsLabel->hide();
91 mCrsSelector->hide();
92 }
93 if ( !( mOptions & Option::Fields ) )
94 mAttributesSelection->hide();
95
96 if ( !( mOptions & Option::SelectedOnly ) )
97 mSelectedOnly->hide();
98
99 if ( !( mOptions & Option::AddToCanvas ) )
100 mAddToCanvas->hide();
101
102 if ( !( mOptions & Option::GeometryType ) )
103 mGeometryGroupBox->hide();
104
105 if ( !( mOptions & Option::Extent ) )
106 mExtentGroupBox->hide();
107
108 if ( !( mOptions & Option::Metadata ) )
109 {
110 mCheckPersistMetadata->setChecked( false );
111 mCheckPersistMetadata->hide();
112 }
113
114 mSelectedOnly->setEnabled( layer && layer->selectedFeatureCount() != 0 );
115 mButtonBox->button( QDialogButtonBox::Ok )->setDisabled( true );
116}
117
118void QgsVectorLayerSaveAsDialog::setup()
119{
120 setupUi( this );
122
123 connect( mFormatComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsVectorLayerSaveAsDialog::mFormatComboBox_currentIndexChanged );
124 connect( mCrsSelector, &QgsProjectionSelectionWidget::crsChanged, this, &QgsVectorLayerSaveAsDialog::mCrsSelector_crsChanged );
125 connect( mSymbologyExportComboBox, &QComboBox::currentTextChanged, this, &QgsVectorLayerSaveAsDialog::mSymbologyExportComboBox_currentIndexChanged );
126 connect( mGeometryTypeComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsVectorLayerSaveAsDialog::mGeometryTypeComboBox_currentIndexChanged );
127 connect( mSelectAllAttributes, &QPushButton::clicked, this, &QgsVectorLayerSaveAsDialog::mSelectAllAttributes_clicked );
128 connect( mDeselectAllAttributes, &QPushButton::clicked, this, &QgsVectorLayerSaveAsDialog::mDeselectAllAttributes_clicked );
129 connect( mUseAliasesForExportedName, &QCheckBox::stateChanged, this, &QgsVectorLayerSaveAsDialog::mUseAliasesForExportedName_stateChanged );
130 connect( mReplaceRawFieldValues, &QCheckBox::stateChanged, this, &QgsVectorLayerSaveAsDialog::mReplaceRawFieldValues_stateChanged );
131 connect( mAttributeTable, &QTableWidget::itemChanged, this, &QgsVectorLayerSaveAsDialog::mAttributeTable_itemChanged );
132
133#ifdef Q_OS_WIN
134 mHelpButtonBox->setVisible( false );
135 mButtonBox->addButton( QDialogButtonBox::Help );
136 connect( mButtonBox, &QDialogButtonBox::helpRequested, this, &QgsVectorLayerSaveAsDialog::showHelp );
137#else
138 connect( mHelpButtonBox, &QDialogButtonBox::helpRequested, this, &QgsVectorLayerSaveAsDialog::showHelp );
139#endif
140 connect( mButtonBox, &QDialogButtonBox::accepted, this, &QgsVectorLayerSaveAsDialog::accept );
141 connect( mButtonBox, &QDialogButtonBox::rejected, this, &QgsVectorLayerSaveAsDialog::reject );
142
143 const QList<QgsVectorFileWriter::DriverDetails> drivers = QgsVectorFileWriter::ogrDriverList();
144 mFormatComboBox->blockSignals( true );
145 for ( const QgsVectorFileWriter::DriverDetails &driver : drivers )
146 {
147 mFormatComboBox->addItem( driver.longName, driver.driverName );
148 }
149
150 QgsSettings settings;
151 QString format = settings.value( u"UI/lastVectorFormat"_s, "GPKG" ).toString();
152 mFormatComboBox->setCurrentIndex( mFormatComboBox->findData( format ) );
153 mFormatComboBox->blockSignals( false );
154
155 const auto addGeomItem = [this]( Qgis::WkbType type ) {
156 mGeometryTypeComboBox->addItem( QgsIconUtils::iconForWkbType( type ), QgsWkbTypes::translatedDisplayString( type ), static_cast<quint32>( type ) );
157 };
158
159 //add geometry types to combobox
160 mGeometryTypeComboBox->addItem( tr( "Automatic" ), -1 );
161 addGeomItem( Qgis::WkbType::Point );
162 addGeomItem( Qgis::WkbType::LineString );
163 addGeomItem( Qgis::WkbType::Polygon );
164 mGeometryTypeComboBox->addItem( QgsWkbTypes::translatedDisplayString( Qgis::WkbType::GeometryCollection ), static_cast<quint32>( Qgis::WkbType::GeometryCollection ) );
165 addGeomItem( Qgis::WkbType::NoGeometry );
166 mGeometryTypeComboBox->setCurrentIndex( mGeometryTypeComboBox->findData( -1 ) );
167
168 mEncodingComboBox->addItems( QgsVectorDataProvider::availableEncodings() );
169
170 QString enc = settings.value( u"UI/encoding"_s, "System" ).toString();
171 int idx = mEncodingComboBox->findText( enc );
172 if ( idx < 0 )
173 {
174 mEncodingComboBox->insertItem( 0, enc );
175 idx = 0;
176 }
177
178 mCrsSelector->setCrs( mSelectedCrs );
179 mCrsSelector->setLayerCrs( mSelectedCrs );
180 mCrsSelector->setMessage( tr(
181 "Select the coordinate reference system for the vector file. "
182 "The data points will be transformed from the layer coordinate reference system."
183 ) );
184 mUserDefinedCrs = mSelectedCrs;
185
186 mEncodingComboBox->setCurrentIndex( idx );
187 mFormatComboBox_currentIndexChanged( mFormatComboBox->currentIndex() );
188
189 //symbology export combo box
190 mSymbologyExportComboBox->addItem( tr( "No Symbology" ), QVariant::fromValue( Qgis::FeatureSymbologyExport::NoSymbology ) );
191 mSymbologyExportComboBox->addItem( tr( "Feature Symbology" ), QVariant::fromValue( Qgis::FeatureSymbologyExport::PerFeature ) );
192 mSymbologyExportComboBox->addItem( tr( "Symbol Layer Symbology" ), QVariant::fromValue( Qgis::FeatureSymbologyExport::PerSymbolLayer ) );
193 mSymbologyExportComboBox_currentIndexChanged( mSymbologyExportComboBox->currentText() );
194
195 // extent group box
196 mExtentGroupBox->setOutputCrs( mSelectedCrs );
197 mExtentGroupBox->setOriginalExtent( mLayerExtent, mSelectedCrs );
198 mExtentGroupBox->setOutputExtentFromOriginal();
199 mExtentGroupBox->setCheckable( true );
200 mExtentGroupBox->setChecked( false );
201 mExtentGroupBox->setCollapsed( true );
202
203 mFilename->setStorageMode( QgsFileWidget::SaveFile );
204 mFilename->setDialogTitle( tr( "Save Layer As" ) );
205 mFilename->setDefaultRoot( settings.value( u"UI/lastVectorFileFilterDir"_s, QDir::homePath() ).toString() );
206 mFilename->setConfirmOverwrite( false );
207 connect( mFilename, &QgsFileWidget::fileChanged, this, [this]( const QString &filePath ) {
208 QgsSettings settings;
209 QFileInfo tmplFileInfo( filePath );
210 settings.setValue( u"UI/lastVectorFileFilterDir"_s, tmplFileInfo.absolutePath() );
211
212 const QFileInfo fileInfo( filePath );
213 const QString suggestedLayerName = QgsMapLayerUtils::launderLayerName( fileInfo.completeBaseName() );
214 if ( mDefaultOutputLayerNameFromInputLayerName.isEmpty() )
215 leLayername->setDefaultValue( suggestedLayerName );
216
217 // if no layer name set, then automatically match the output layer name to the file name
218 if ( leLayername->text().isEmpty() && !filePath.isEmpty() && leLayername->isEnabled() )
219 {
220 leLayername->setText( suggestedLayerName );
221 }
222 mButtonBox->button( QDialogButtonBox::Ok )->setEnabled( !filePath.isEmpty() );
223 } );
224
225 try
226 {
227 const QgsDatumEnsemble ensemble = mSelectedCrs.datumEnsemble();
228 if ( ensemble.isValid() )
229 {
230 mCrsSelector->setSourceEnsemble( ensemble.name() );
231 }
232 }
233 catch ( QgsNotSupportedException & )
234 {}
235
236 mCrsSelector->setShowAccuracyWarnings( true );
237}
238
239QList<QPair<QLabel *, QWidget *>> QgsVectorLayerSaveAsDialog::createControls( const QMap<QString, QgsVectorFileWriter::Option *> &options )
240{
241 QList<QPair<QLabel *, QWidget *>> controls;
242 QMap<QString, QgsVectorFileWriter::Option *>::ConstIterator it;
243
244 for ( it = options.constBegin(); it != options.constEnd(); ++it )
245 {
246 QgsVectorFileWriter::Option *option = it.value();
247 QWidget *control = nullptr;
248 switch ( option->type )
249 {
251 {
252 QgsVectorFileWriter::IntOption *opt = dynamic_cast<QgsVectorFileWriter::IntOption *>( option );
253 if ( opt )
254 {
255 QSpinBox *sb = new QSpinBox();
256 sb->setObjectName( it.key() );
257 sb->setMaximum( std::numeric_limits<int>::max() ); // the default is 99
258 sb->setMinimum( std::min( 0, opt->defaultValue ) ); // allow negative (default) values i.e. ZSTD default compression level -1
259 sb->setValue( opt->defaultValue );
260 control = sb;
261 }
262 break;
263 }
264
266 {
267 QgsVectorFileWriter::SetOption *opt = dynamic_cast<QgsVectorFileWriter::SetOption *>( option );
268 if ( opt )
269 {
270 QComboBox *cb = new QComboBox();
271 cb->setObjectName( it.key() );
272 for ( const QString &val : std::as_const( opt->values ) )
273 {
274 cb->addItem( val, val );
275 }
276 if ( opt->allowNone )
277 cb->addItem( tr( "<Default>" ), QgsVariantUtils::createNullVariant( QMetaType::Type::QString ) );
278 int idx = cb->findText( opt->defaultValue );
279 if ( idx == -1 )
280 idx = cb->findData( QgsVariantUtils::createNullVariant( QMetaType::Type::QString ) );
281 cb->setCurrentIndex( idx );
282 control = cb;
283 }
284 break;
285 }
286
288 {
289 QgsVectorFileWriter::StringOption *opt = dynamic_cast<QgsVectorFileWriter::StringOption *>( option );
290 if ( opt )
291 {
292 QLineEdit *le = new QLineEdit( opt->defaultValue );
293 le->setObjectName( it.key() );
294 control = le;
295 }
296 break;
297 }
298
300 control = nullptr;
301 break;
302 }
303
304 if ( control )
305 {
306 QLabel *label = new QLabel( it.key() );
307
308 // Pack the tooltip in some html element, so it gets linebreaks.
309 label->setToolTip( u"<p>%1</p>"_s.arg( option->docString.toHtmlEscaped() ) );
310 control->setToolTip( u"<p>%1</p>"_s.arg( option->docString.toHtmlEscaped() ) );
311
312 controls << QPair<QLabel *, QWidget *>( label, control );
313 }
314 }
315
316 return controls;
317}
318
320{
321#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION( 3, 9, 0 )
322 if ( format() == "OpenFileGDB"_L1 )
323 {
324 // The OpenFileGDB driver supports 64-bit integer fields starting with GDAL 3.9,
325 // if selecting the TARGET_ARCGIS_VERSION=ARCGIS_PRO_3_2_OR_LATER option
326 bool targetAll = true;
327 for ( const QString &layerOption : layerOptions() )
328 {
329 if ( layerOption == "TARGET_ARCGIS_VERSION=ARCGIS_PRO_3_2_OR_LATER"_L1 )
330 {
331 targetAll = false;
332 }
333 }
334 if ( targetAll )
335 {
336 const QgsAttributeList attributesSelected = selectedAttributes();
337 for ( int i = 0; i < attributesSelected.size(); ++i )
338 {
339 QgsField fld = mLayer->fields().at( attributesSelected.at( i ) );
340 if ( fld.type() == QMetaType::Type::LongLong )
341 {
342 if ( QMessageBox::question( this, tr( "Save Vector Layer As" ), tr( "The layer contains at least one 64-bit integer field, which, with the current settings, can only be exported as a Real field. It could be exported as a 64-bit integer field if the TARGET_ARCGIS_VERSION layer option is set to ARCGIS_PRO_3_2_OR_LATER. Do you want to continue and export it as a Real field?" ) )
343 != QMessageBox::Yes )
344 {
345 return;
346 }
347 break;
348 }
349 }
350 }
351 }
352 else if ( format() == "FileGDB"_L1 )
353 {
354 // The FileGDB driver based on the ESRI SDK doesn't support 64-bit integers
355 const QgsAttributeList attributesSelected = selectedAttributes();
356 for ( int i = 0; i < attributesSelected.size(); ++i )
357 {
358 QgsField fld = mLayer->fields().at( attributesSelected.at( i ) );
359 if ( fld.type() == QMetaType::Type::LongLong )
360 {
361 if ( QMessageBox::question(
362 this,
363 tr( "Save Vector Layer As" ),
364 tr(
365 "The layer contains at least one 64-bit integer field, which cannot be exported as such when using this output driver. 64-bit integer fields could be supported by selecting the %1 "
366 "format and setting its TARGET_ARCGIS_VERSION layer option to ARCGIS_PRO_3_2_OR_LATER. Do you want to continue and export it as a Real field?"
367 )
368 .arg( tr( "ESRI File Geodatabase" ) )
369 )
370 != QMessageBox::Yes )
371 {
372 return;
373 }
374 break;
375 }
376 }
377 }
378#endif
379
380 if ( QFile::exists( fileName() ) )
381 {
384 QMessageBox msgBox;
385 msgBox.setIcon( QMessageBox::Question );
386 msgBox.setWindowTitle( tr( "Save Vector Layer As" ) );
387 QPushButton *overwriteFileButton = msgBox.addButton( tr( "Overwrite File" ), QMessageBox::ActionRole );
388 QPushButton *overwriteLayerButton = msgBox.addButton( tr( "Overwrite Layer" ), QMessageBox::ActionRole );
389 QPushButton *appendToLayerButton = msgBox.addButton( tr( "Append to Layer" ), QMessageBox::ActionRole );
390 msgBox.setStandardButtons( QMessageBox::Cancel );
391 msgBox.setDefaultButton( QMessageBox::Cancel );
392 overwriteFileButton->hide();
393 overwriteLayerButton->hide();
394 appendToLayerButton->hide();
395 if ( layerExists )
396 {
398 {
399 msgBox.setText( tr( "The layer already exists. Do you want to overwrite the whole file or overwrite the layer?" ) );
400 overwriteFileButton->setVisible( true );
401 overwriteLayerButton->setVisible( true );
402 }
404 {
405 msgBox.setText( tr( "The file already exists. Do you want to overwrite it?" ) );
406 overwriteFileButton->setVisible( true );
407 }
409 {
410 msgBox.setText( tr( "The layer already exists. Do you want to overwrite the whole file, overwrite the layer or append features to the layer?" ) );
411 appendToLayerButton->setVisible( true );
412 overwriteFileButton->setVisible( true );
413 overwriteLayerButton->setVisible( true );
414 }
415 else
416 {
417 msgBox.setText( tr( "The layer already exists. Do you want to overwrite the whole file or append features to the layer?" ) );
418 appendToLayerButton->setVisible( true );
419 overwriteFileButton->setVisible( true );
420 }
421
422 int ret = msgBox.exec();
423 if ( ret == QMessageBox::Cancel )
424 return;
425 if ( msgBox.clickedButton() == overwriteFileButton )
426 mActionOnExistingFile = QgsVectorFileWriter::CreateOrOverwriteFile;
427 else if ( msgBox.clickedButton() == overwriteLayerButton )
428 mActionOnExistingFile = QgsVectorFileWriter::CreateOrOverwriteLayer;
429 else if ( msgBox.clickedButton() == appendToLayerButton )
430 mActionOnExistingFile = QgsVectorFileWriter::AppendToLayerNoNewFields;
431 }
432 else // !layerExists
433 {
435 {
436 mActionOnExistingFile = QgsVectorFileWriter::CreateOrOverwriteLayer;
437 }
438 else
439 {
440 // should not reach here, layer does not exist and cannot add new layer
441 if ( QMessageBox::question( this, tr( "Save Vector Layer As" ), tr( "The file already exists. Do you want to overwrite it?" ) ) != QMessageBox::Yes )
442 {
443 return;
444 }
445 mActionOnExistingFile = QgsVectorFileWriter::CreateOrOverwriteFile;
446 }
447 }
448 }
449
450 if ( mActionOnExistingFile == QgsVectorFileWriter::AppendToLayerNoNewFields )
451 {
453 {
454 if ( QMessageBox::question( this, tr( "Save Vector Layer As" ), tr( "The existing layer has additional fields. Do you want to add the missing fields to the layer?" ) ) == QMessageBox::Yes )
455 {
456 mActionOnExistingFile = QgsVectorFileWriter::AppendToLayerAddFields;
457 }
458 }
459 }
460 else if ( mActionOnExistingFile == QgsVectorFileWriter::CreateOrOverwriteFile && QFile::exists( fileName() ) )
461 {
462 const QList<QgsProviderSublayerDetails> sublayers = QgsProviderRegistry::instance()->querySublayers( fileName() );
463 QStringList layerList;
464 layerList.reserve( sublayers.size() );
465 for ( const QgsProviderSublayerDetails &sublayer : sublayers )
466 {
467 layerList.append( sublayer.name() );
468 }
469 if ( layerList.length() > 1 )
470 {
471 layerList.sort( Qt::CaseInsensitive );
472 QMessageBox msgBox;
473 msgBox.setIcon( QMessageBox::Warning );
474 msgBox.setWindowTitle( tr( "Overwrite File" ) );
475 msgBox.setText( tr( "This file contains %1 layers that will be lost!\n" ).arg( QLocale().toString( layerList.length() ) ) );
476 msgBox.setDetailedText( tr( "The following layers will be permanently lost:\n\n%1" ).arg( layerList.join( "\n" ) ) );
477 msgBox.setStandardButtons( QMessageBox::Ok | QMessageBox::Cancel );
478 if ( msgBox.exec() == QMessageBox::Cancel )
479 return;
480 }
481 }
482
483 QgsSettings settings;
484 settings.setValue( u"UI/lastVectorFileFilterDir"_s, QFileInfo( fileName() ).absolutePath() );
485 settings.setValue( u"UI/lastVectorFormat"_s, format() );
486 settings.setValue( u"UI/encoding"_s, encoding() );
487 QDialog::accept();
488}
489
490void QgsVectorLayerSaveAsDialog::mFormatComboBox_currentIndexChanged( int idx )
491{
492 Q_UNUSED( idx )
493
494 mFilename->setEnabled( true );
495 QString filter = QgsVectorFileWriter::filterForDriver( format() );
496 // A bit of hack to solve https://github.com/qgis/QGIS/issues/54566
497 // to be able to select an existing File Geodatabase, we add in the filter
498 // the "gdb" file that is found in all File Geodatabase .gdb directory
499 // to allow the user to select it. We need to detect this particular case
500 // in QgsFileWidget::openFileDialog() to remove this gdb file from the
501 // selected filename
502 if ( format() == "OpenFileGDB"_L1 || format() == "FileGDB"_L1 )
503 filter = u"%1 (*.gdb *.GDB gdb)"_s.arg( tr( "ESRI File Geodatabase" ) );
504 mFilename->setFilter( filter );
505
506 // if output filename already defined we need to replace old suffix
507 // to avoid double extensions like .gpkg.shp
508 if ( !mFilename->filePath().isEmpty() )
509 {
510 const thread_local QRegularExpression rx( "\\.(.*?)[\\s]" );
511 const QString ext = rx.match( filter ).captured( 1 );
512 if ( !ext.isEmpty() )
513 {
514 QFileInfo fi( mFilename->filePath() );
515 mFilename->setFilePath( u"%1/%2.%3"_s.arg( fi.path(), fi.baseName(), ext ) );
516 }
517 }
518
519 bool selectAllFields = true;
520
521 // Is it a format for which fields that have attached widgets of types
522 // ValueMap, ValueRelation, etc. should be by default exported with their displayed
523 // values
524 bool isFormatForFieldsAsDisplayedValues = false;
525
526 const QString sFormat( format() );
527 if ( sFormat == "DXF"_L1 || sFormat == "DGN"_L1 )
528 {
529 mAttributesSelection->setVisible( false );
530 selectAllFields = false;
531 }
532 else
533 {
534 if ( mOptions & Option::Fields )
535 {
536 mAttributesSelection->setVisible( true );
537 isFormatForFieldsAsDisplayedValues = ( sFormat == "CSV"_L1 || sFormat == "XLS"_L1 || sFormat == "XLSX"_L1 || sFormat == "ODS"_L1 );
538 }
539 }
540
541 // Show symbology options only for some formats
542 if ( QgsVectorFileWriter::supportsFeatureStyles( sFormat ) && ( mOptions & Option::Symbology ) )
543 {
544 mSymbologyExportLabel->setVisible( true );
545 mSymbologyExportComboBox->setVisible( true );
546 mScaleLabel->setVisible( true );
547 mScaleWidget->setVisible( true );
548 }
549 else
550 {
551 mSymbologyExportLabel->hide();
552 mSymbologyExportComboBox->hide();
553 mScaleLabel->hide();
554 mScaleWidget->hide();
555 }
556
557 leLayername->setEnabled(
558 sFormat == "KML"_L1 || sFormat == "GPKG"_L1 || sFormat == "XLSX"_L1 || sFormat == "ODS"_L1 || sFormat == "FileGDB"_L1 || sFormat == "OpenFileGDB"_L1 || sFormat == "SQLite"_L1 || sFormat == "SpatiaLite"_L1
559 );
560
561 if ( sFormat == "XLSX"_L1 )
562 leLayername->setMaxLength( 31 );
563 else if ( leLayername->isEnabled() )
564 leLayername->setMaxLength( 32767 ); // default length
565
566 if ( !leLayername->isEnabled() )
567 leLayername->setText( QString() );
568 else if ( leLayername->text().isEmpty() )
569 {
570 QString layerName = mDefaultOutputLayerNameFromInputLayerName;
571 if ( layerName.isEmpty() && !mFilename->filePath().isEmpty() )
572 {
573 layerName = QFileInfo( mFilename->filePath() ).baseName();
574 leLayername->setDefaultValue( layerName );
575 }
576 if ( layerName.isEmpty() )
577 layerName = tr( "new_layer" );
578 leLayername->setText( layerName );
579 }
580
581 if ( mLayer )
582 {
583 // save fields state: if it is checked and should be exported as displayed value
584 const bool isPreviousFormatForFieldsAsDisplayedValues = ( mPreviousFormat == "CSV"_L1 || mPreviousFormat == "XLS"_L1 || mPreviousFormat == "XLSX"_L1 || mPreviousFormat == "ODS"_L1 );
585 for ( int i = 0; i < mAttributeTable->rowCount(); i++ )
586 {
587 QTableWidgetItem *nameItem = mAttributeTable->item( i, static_cast<int>( ColumnIndex::Name ) );
588 if ( !nameItem )
589 {
590 continue;
591 }
592
593 const QString fieldName = nameItem->text();
594 const bool exportField = ( nameItem->checkState() == Qt::Checked );
595
596 std::optional<bool> exportAsValue = std::nullopt;
597 if ( isPreviousFormatForFieldsAsDisplayedValues )
598 {
599 QTableWidgetItem *valueItem = mAttributeTable->item( i, static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) );
600 if ( valueItem && ( valueItem->flags() & Qt::ItemIsUserCheckable ) )
601 {
602 exportAsValue = ( valueItem->checkState() == Qt::Checked );
603 }
604 }
605 else
606 {
607 exportAsValue = mFieldsState[fieldName].second;
608 }
609
610 mFieldsState[fieldName] = { exportField, exportAsValue };
611 }
612
613 mPreviousFormat = sFormat;
614
615 mAttributeTable->setRowCount( mLayer->fields().count() );
616
617 QStringList horizontalHeaders = QStringList() << tr( "Name" ) << tr( "Export name" ) << tr( "Type" ) << tr( "Replace with displayed values" );
618 mAttributeTable->setColumnCount( static_cast<int>( horizontalHeaders.size() ) );
619 mAttributeTable->setHorizontalHeaderLabels( horizontalHeaders );
620
621 bool foundFieldThatCanBeExportedAsDisplayedValue = false;
622 for ( int i = 0; i < mLayer->fields().size(); ++i )
623 {
624 const QgsEditorWidgetSetup setup = QgsGui::editorWidgetRegistry()->findBest( mLayer, mLayer->fields()[i].name() );
625 if ( setup.type() != "TextEdit"_L1 && QgsGui::editorWidgetRegistry()->factory( setup.type() ) )
626 {
627 foundFieldThatCanBeExportedAsDisplayedValue = true;
628 break;
629 }
630 }
631 mAttributeTable->setColumnHidden( static_cast<int>( ColumnIndex::ExportAsDisplayedValue ), !foundFieldThatCanBeExportedAsDisplayedValue );
632
633 bool allChecked = true;
634 bool allUnchecked = true;
635 bool anyEnabled = false;
636
637 const QSignalBlocker signalBlockerAttributeTable( mAttributeTable );
638 {
639 for ( int i = 0; i < mLayer->fields().size(); ++i )
640 {
641 QgsField fld = mLayer->fields().at( i );
642 Qt::ItemFlags flags = mLayer->providerType() != "oracle"_L1 || !fld.typeName().contains( "SDO_GEOMETRY"_L1 ) ? Qt::ItemIsEnabled : Qt::NoItemFlags;
643 QTableWidgetItem *item = nullptr;
644 const QString fieldName = fld.name();
645 const bool exportField = mFieldsState.contains( fieldName ) ? mFieldsState[fieldName].first : selectAllFields;
646 item = new QTableWidgetItem( fieldName );
647 item->setFlags( flags | Qt::ItemIsUserCheckable );
648 item->setCheckState( ( exportField ) ? Qt::Checked : Qt::Unchecked );
649 mAttributeTable->setItem( i, static_cast<int>( ColumnIndex::Name ), item );
650
651 item = new QTableWidgetItem( fieldName );
652 item->setFlags( flags | Qt::ItemIsEditable );
653 item->setData( Qt::UserRole, fld.displayName() );
654 mAttributeTable->setItem( i, static_cast<int>( ColumnIndex::ExportName ), item );
655
656 item = new QTableWidgetItem( fld.typeName() );
657 item->setFlags( flags );
658 mAttributeTable->setItem( i, static_cast<int>( ColumnIndex::Type ), item );
659
660 if ( foundFieldThatCanBeExportedAsDisplayedValue )
661 {
662 const QgsEditorWidgetSetup setup = QgsGui::editorWidgetRegistry()->findBest( mLayer, mLayer->fields()[i].name() );
663 QgsEditorWidgetFactory *factory = nullptr;
664 const QString widgetId( setup.type() );
665 if ( flags == Qt::ItemIsEnabled && widgetId != "TextEdit"_L1 && ( factory = QgsGui::editorWidgetRegistry()->factory( widgetId ) ) )
666 {
667 item = new QTableWidgetItem( tr( "Use %1" ).arg( factory->name() ) );
668 bool exportAsValue;
669 if ( mFieldsState.contains( fieldName ) && mFieldsState[fieldName].second.has_value() )
670 {
671 exportAsValue = mFieldsState[fieldName].second.value();
672 }
673 else
674 {
675 exportAsValue
676 = ( exportField && isFormatForFieldsAsDisplayedValues && ( widgetId == "ValueMap"_L1 || widgetId == "ValueRelation"_L1 || widgetId == "CheckBox"_L1 || widgetId == "RelationReference"_L1 ) );
677 }
678 item->setFlags( ( exportField ) ? ( Qt::ItemIsEnabled | Qt::ItemIsUserCheckable ) : Qt::ItemIsUserCheckable );
679 item->setCheckState( exportAsValue ? Qt::Checked : Qt::Unchecked );
680 mAttributeTable->setItem( i, static_cast<int>( ColumnIndex::ExportAsDisplayedValue ), item );
681
682 // collect information for setting mReplaceRawFieldValues state
683 if ( exportField )
684 {
685 anyEnabled = true;
686 if ( exportAsValue )
687 allUnchecked = false;
688 else
689 allChecked = false;
690 }
691 }
692 else
693 {
694 item = new QTableWidgetItem();
695 item->setFlags( Qt::NoItemFlags );
696 mAttributeTable->setItem( i, static_cast<int>( ColumnIndex::ExportAsDisplayedValue ), item );
697 }
698 }
699 }
700 }
701
702 Qt::CheckState replaceRawState;
703 if ( !anyEnabled || allUnchecked )
704 replaceRawState = Qt::Unchecked;
705 else if ( allChecked )
706 replaceRawState = Qt::Checked;
707 else
708 replaceRawState = Qt::PartiallyChecked;
709
710 whileBlocking( mReplaceRawFieldValues )->setCheckState( replaceRawState );
711 mReplaceRawFieldValues->setEnabled( selectAllFields && anyEnabled );
712 mReplaceRawFieldValues->setVisible( foundFieldThatCanBeExportedAsDisplayedValue );
713
714 mAttributeTable->resizeColumnsToContents();
715 }
716
717 QgsVectorFileWriter::MetaData driverMetaData;
718
719 while ( mDatasourceOptionsGroupBox->layout()->count() )
720 {
721 QLayoutItem *item = mDatasourceOptionsGroupBox->layout()->takeAt( 0 );
722 delete item->widget();
723 delete item;
724 }
725
726 while ( mLayerOptionsGroupBox->layout()->count() )
727 {
728 QLayoutItem *item = mLayerOptionsGroupBox->layout()->takeAt( 0 );
729 delete item->widget();
730 delete item;
731 }
732
733 typedef QPair<QLabel *, QWidget *> LabelControlPair;
734
735 if ( QgsVectorFileWriter::driverMetadata( sFormat, driverMetaData ) )
736 {
737 if ( !driverMetaData.driverOptions.empty() )
738 {
739 mDatasourceOptionsGroupBox->setVisible( true );
740 QList<QPair<QLabel *, QWidget *>> controls = createControls( driverMetaData.driverOptions );
741
742 QFormLayout *datasourceLayout = dynamic_cast<QFormLayout *>( mDatasourceOptionsGroupBox->layout() );
743
744 const auto constControls = controls;
745 for ( LabelControlPair control : constControls )
746 {
747 datasourceLayout->addRow( control.first, control.second );
748 }
749 }
750 else
751 {
752 mDatasourceOptionsGroupBox->setVisible( false );
753 }
754
755 if ( !driverMetaData.layerOptions.empty() )
756 {
757 mLayerOptionsGroupBox->setVisible( true );
758 QList<QPair<QLabel *, QWidget *>> controls = createControls( driverMetaData.layerOptions );
759
760 QFormLayout *layerOptionsLayout = dynamic_cast<QFormLayout *>( mLayerOptionsGroupBox->layout() );
761
762 const auto constControls = controls;
763 for ( LabelControlPair control : constControls )
764 {
765 layerOptionsLayout->addRow( control.first, control.second );
766 }
767
768 // for GeoJSON we need to track changes of the RFC7946 option to update CRS accordingly
769 if ( sFormat == "GeoJSON"_L1 )
770 {
771 if ( QComboBox *cmbRfc7946 = mLayerOptionsGroupBox->findChild<QComboBox *>( "RFC7946"_L1 ) )
772 {
773 connect( cmbRfc7946, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsVectorLayerSaveAsDialog::setCrsForFormat );
774 }
775 }
776 }
777 else
778 {
779 mLayerOptionsGroupBox->setVisible( false );
780 }
781
782 if ( driverMetaData.compulsoryEncoding.isEmpty() )
783 {
784 mEncodingComboBox->setEnabled( true );
785 }
786 else
787 {
788 int idx = mEncodingComboBox->findText( driverMetaData.compulsoryEncoding );
789 if ( idx >= 0 )
790 {
791 mEncodingComboBox->setCurrentIndex( idx );
792 mEncodingComboBox->setDisabled( true );
793 }
794 else
795 {
796 mEncodingComboBox->setEnabled( true );
797 }
798 }
799 }
800 else
801 {
802 mEncodingComboBox->setEnabled( true );
803 }
804
805 GDALDriverH hDriver = GDALGetDriverByName( format().toUtf8().constData() );
806 if ( hDriver )
807 {
808 const bool canReopen = GDALGetMetadataItem( hDriver, GDAL_DCAP_OPEN, nullptr );
809 if ( mAddToCanvas->isEnabled() && !canReopen )
810 {
811 mAddToCanvasStateOnOpenCompatibleDriver = mAddToCanvas->isChecked();
812 mAddToCanvas->setChecked( false );
813 mAddToCanvas->setEnabled( false );
814 }
815 else if ( !mAddToCanvas->isEnabled() && canReopen )
816 {
817 mAddToCanvas->setChecked( mAddToCanvasStateOnOpenCompatibleDriver );
818 mAddToCanvas->setEnabled( true );
819 }
820 }
821
822 // update CRS selector based on the selected format and layer creation options
823 setCrsForFormat();
824}
825
826void QgsVectorLayerSaveAsDialog::mUseAliasesForExportedName_stateChanged( int state )
827{
828 const QSignalBlocker signalBlocker( mAttributeTable );
829
830 switch ( state )
831 {
832 case Qt::Unchecked:
833 {
834 // Check for modified entries
835 bool modifiedEntries = false;
836 for ( int i = 0; i < mAttributeTable->rowCount(); i++ )
837 {
838 if ( mAttributeTable->item( i, static_cast<int>( ColumnIndex::ExportName ) )->text() != mAttributeTable->item( i, static_cast<int>( ColumnIndex::ExportName ) )->data( Qt::UserRole ).toString() )
839 {
840 modifiedEntries = true;
841 break;
842 }
843 }
844
845 if ( modifiedEntries )
846 {
847 if ( QMessageBox::question( this, tr( "Modified names" ), tr( "Some names were modified and will be overridden. Do you want to continue?" ) ) == QMessageBox::No )
848 {
849 whileBlocking( mUseAliasesForExportedName )->setCheckState( Qt::PartiallyChecked );
850 return;
851 }
852 }
853
854 for ( int i = 0; i < mAttributeTable->rowCount(); i++ )
855 {
856 mUseAliasesForExportedName->setTristate( false );
857 mAttributeTable->item( i, static_cast<int>( ColumnIndex::ExportName ) )->setText( mAttributeTable->item( i, static_cast<int>( ColumnIndex::Name ) )->text() );
858 }
859 }
860 break;
861 case Qt::Checked:
862 {
863 // Check for modified entries
864 bool modifiedEntries = false;
865 for ( int i = 0; i < mAttributeTable->rowCount(); i++ )
866 {
867 if ( mAttributeTable->item( i, static_cast<int>( ColumnIndex::ExportName ) )->text() != mAttributeTable->item( i, static_cast<int>( ColumnIndex::Name ) )->text() )
868 modifiedEntries = true;
869 }
870
871 if ( modifiedEntries )
872 {
873 if ( QMessageBox::question( this, tr( "Modified names" ), tr( "Some names were modified and will be overridden. Do you want to continue?" ) ) == QMessageBox::No )
874 {
875 whileBlocking( mUseAliasesForExportedName )->setCheckState( Qt::PartiallyChecked );
876 return;
877 }
878 }
879
880 for ( int i = 0; i < mAttributeTable->rowCount(); i++ )
881 {
882 mUseAliasesForExportedName->setTristate( false );
883 const QString alias = mAttributeTable->item( i, static_cast<int>( ColumnIndex::ExportName ) )->data( Qt::UserRole ).toString();
884 mAttributeTable->item( i, static_cast<int>( ColumnIndex::ExportName ) )->setText( alias );
885 }
886 }
887 break;
888 case Qt::PartiallyChecked:
889 // Do nothing
890 break;
891 }
892}
893
894void QgsVectorLayerSaveAsDialog::mReplaceRawFieldValues_stateChanged( int )
895{
896 if ( mAttributeTable->isColumnHidden( static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) ) )
897 return;
898
899 const QSignalBlocker signalBlockerAttributeTable( mAttributeTable );
900 const QSignalBlocker signalBlockerReplaceRawFieldValues( mReplaceRawFieldValues );
901
902 if ( mReplaceRawFieldValues->checkState() != Qt::PartiallyChecked )
903 {
904 for ( int i = 0; i < mAttributeTable->rowCount(); i++ )
905 {
906 if ( mAttributeTable->item( i, static_cast<int>( ColumnIndex::Name ) )->checkState() == Qt::Checked
907 && mAttributeTable->item( i, static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) )
908 && mAttributeTable->item( i, static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) )->flags() & Qt::ItemIsEnabled )
909 {
910 mAttributeTable->item( i, static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) )->setCheckState( mReplaceRawFieldValues->checkState() );
911 }
912 }
913 }
914 mReplaceRawFieldValues->setTristate( false );
915}
916
917void QgsVectorLayerSaveAsDialog::mAttributeTable_itemChanged( QTableWidgetItem *item )
918{
919 const QSignalBlocker signalBlockerAttributeTable( mAttributeTable );
920 const QSignalBlocker signalBlockerReplaceRawFieldValues( mReplaceRawFieldValues );
921
922 int row = item->row();
923 int column = item->column();
924
925 switch ( static_cast<ColumnIndex>( column ) )
926 {
927 case ColumnIndex::Name:
928 {
929 if ( mAttributeTable->isColumnHidden( static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) )
930 || !mAttributeTable->item( row, static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) )
931 || !( mAttributeTable->item( row, static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) )->flags() & Qt::ItemIsUserCheckable ) )
932 return;
933
934 if ( mAttributeTable->item( row, column )->checkState() == Qt::Unchecked )
935 {
936 mAttributeTable->item( row, static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) )->setCheckState( Qt::Unchecked );
937 mAttributeTable->item( row, static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) )->setFlags( Qt::ItemIsUserCheckable );
938 bool checkBoxEnabled = false;
939 for ( int i = 0; i < mAttributeTable->rowCount(); i++ )
940 {
941 if ( mAttributeTable->item( i, static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) )
942 && mAttributeTable->item( i, static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) )->flags() & Qt::ItemIsEnabled )
943 {
944 checkBoxEnabled = true;
945 break;
946 }
947 }
948 mReplaceRawFieldValues->setEnabled( checkBoxEnabled );
949 if ( !checkBoxEnabled )
950 mReplaceRawFieldValues->setCheckState( Qt::Unchecked );
951 }
952 else if ( mAttributeTable->item( row, column )->checkState() == Qt::Checked )
953 {
954 mAttributeTable->item( row, static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) )->setFlags( Qt::ItemIsUserCheckable | Qt::ItemIsEnabled );
955 mReplaceRawFieldValues->setEnabled( true );
956 }
957 }
958 break;
959 case ColumnIndex::ExportName:
960 {
961 // Check empty export name
962 if ( item->text().isEmpty() )
963 {
964 QMessageBox::warning( this, tr( "Empty export name" ), tr( "Empty export name are not allowed." ) );
965 return;
966 }
967
968 // Rename eventually duplicated names
969 QStringList names = attributesExportNames();
970 while ( names.count( item->text() ) > 1 )
971 item->setText( QString( "%1_2" ).arg( item->text() ) );
972
973 mUseAliasesForExportedName->setCheckState( Qt::PartiallyChecked );
974 }
975 break;
976 case ColumnIndex::Type:
977 // Nothing to do
978 break;
979 case ColumnIndex::ExportAsDisplayedValue:
980 {
981 if ( mAttributeTable->item( row, column )->flags() & Qt::ItemIsUserCheckable )
982 {
983 bool allChecked = true;
984 bool allUnchecked = true;
985 for ( int i = 0; i < mAttributeTable->rowCount(); i++ )
986 {
987 if ( mAttributeTable->item( i, static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) )
988 && mAttributeTable->item( i, static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) )->flags() & Qt::ItemIsEnabled )
989 {
990 if ( mAttributeTable->item( i, static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) )->checkState() == Qt::Unchecked )
991 allChecked = false;
992 else
993 allUnchecked = false;
994 }
995 }
996 mReplaceRawFieldValues->setCheckState( ( !allChecked && !allUnchecked ) ? Qt::PartiallyChecked : ( allChecked ) ? Qt::Checked : Qt::Unchecked );
997 }
998 }
999 break;
1000 }
1001}
1002
1003void QgsVectorLayerSaveAsDialog::mCrsSelector_crsChanged( const QgsCoordinateReferenceSystem &crs )
1004{
1005 if ( mCrsDefinedByFormat )
1006 {
1007 // this should never happen as CRS selector should be disabled, but let's be safe and
1008 // avoid overwriting user defined CRS with the CRS required by the output format
1009 return;
1010 }
1011
1012 mSelectedCrs = crs;
1013 mUserDefinedCrs = crs;
1014 mExtentGroupBox->setOutputCrs( mSelectedCrs );
1015}
1016
1018{
1019 return mFilename->filePath();
1020}
1021
1023{
1024 return leLayername->text();
1025}
1026
1028{
1029 return mEncodingComboBox->currentText();
1030}
1031
1033{
1034 return mFormatComboBox->currentData().toString();
1035}
1036
1038{
1039 return mSelectedCrs;
1040}
1041
1043{
1044 QStringList options;
1045
1046 QgsVectorFileWriter::MetaData driverMetaData;
1047
1048 if ( QgsVectorFileWriter::driverMetadata( format(), driverMetaData ) )
1049 {
1050 QMap<QString, QgsVectorFileWriter::Option *>::ConstIterator it;
1051
1052 for ( it = driverMetaData.driverOptions.constBegin(); it != driverMetaData.driverOptions.constEnd(); ++it )
1053 {
1054 switch ( it.value()->type )
1055 {
1057 {
1059 QSpinBox *sb = mDatasourceOptionsGroupBox->findChild<QSpinBox *>( it.key() );
1060 if ( opt && sb && sb->value() != opt->defaultValue )
1061 options << u"%1=%2"_s.arg( it.key() ).arg( sb->value() );
1062 break;
1063 }
1064
1066 {
1068 QComboBox *cb = mDatasourceOptionsGroupBox->findChild<QComboBox *>( it.key() );
1069 if ( opt && cb && cb->itemData( cb->currentIndex() ) != opt->defaultValue )
1070 options << u"%1=%2"_s.arg( it.key(), cb->currentText() );
1071 break;
1072 }
1073
1075 {
1077 QLineEdit *le = mDatasourceOptionsGroupBox->findChild<QLineEdit *>( it.key() );
1078 if ( opt && le && le->text() != opt->defaultValue )
1079 options << u"%1=%2"_s.arg( it.key(), le->text() );
1080 break;
1081 }
1082
1084 {
1085 QgsVectorFileWriter::HiddenOption *opt = dynamic_cast<QgsVectorFileWriter::HiddenOption *>( it.value() );
1086 if ( opt && !opt->mValue.isEmpty() )
1087 options << u"%1=%2"_s.arg( it.key(), opt->mValue );
1088 break;
1089 }
1090 }
1091 }
1092 }
1093
1094 QString plainText = mOgrDatasourceOptions->toPlainText().trimmed();
1095 if ( !plainText.isEmpty() )
1096 options += plainText.split( '\n' );
1097
1098 return options;
1099}
1100
1102{
1103 QStringList options;
1104
1105 QgsVectorFileWriter::MetaData driverMetaData;
1106
1107 if ( QgsVectorFileWriter::driverMetadata( format(), driverMetaData ) )
1108 {
1109 QMap<QString, QgsVectorFileWriter::Option *>::ConstIterator it;
1110
1111 for ( it = driverMetaData.layerOptions.constBegin(); it != driverMetaData.layerOptions.constEnd(); ++it )
1112 {
1113 switch ( it.value()->type )
1114 {
1116 {
1117 QgsVectorFileWriter::IntOption *opt = qgis::down_cast<QgsVectorFileWriter::IntOption *>( *it );
1118 QSpinBox *sb = mLayerOptionsGroupBox->findChild<QSpinBox *>( it.key() );
1119 if ( opt && sb && sb->value() != opt->defaultValue )
1120 options << u"%1=%2"_s.arg( it.key() ).arg( sb->value() );
1121 break;
1122 }
1123
1125 {
1126 QgsVectorFileWriter::SetOption *opt = qgis::down_cast<QgsVectorFileWriter::SetOption *>( *it );
1127 QComboBox *cb = mLayerOptionsGroupBox->findChild<QComboBox *>( it.key() );
1128 if ( opt && cb && cb->itemData( cb->currentIndex() ) != opt->defaultValue )
1129 options << u"%1=%2"_s.arg( it.key(), cb->currentText() );
1130 break;
1131 }
1132
1134 {
1135 QgsVectorFileWriter::StringOption *opt = qgis::down_cast<QgsVectorFileWriter::StringOption *>( *it );
1136 QLineEdit *le = mLayerOptionsGroupBox->findChild<QLineEdit *>( it.key() );
1137 if ( opt && le && le->text() != opt->defaultValue )
1138 options << u"%1=%2"_s.arg( it.key(), le->text() );
1139 break;
1140 }
1141
1143 {
1144 QgsVectorFileWriter::HiddenOption *opt = qgis::down_cast<QgsVectorFileWriter::HiddenOption *>( it.value() );
1145 if ( !opt->mValue.isEmpty() )
1146 options << u"%1=%2"_s.arg( it.key(), opt->mValue );
1147 break;
1148 }
1149 }
1150 }
1151 }
1152
1153 QString plainText = mOgrLayerOptions->toPlainText().trimmed();
1154 if ( !plainText.isEmpty() )
1155 options += plainText.split( '\n' );
1156
1157 return options;
1158}
1159
1161{
1162 QgsAttributeList attributes;
1163
1164 for ( int i = 0; i < mAttributeTable->rowCount(); i++ )
1165 {
1166 if ( mAttributeTable->item( i, static_cast<int>( ColumnIndex::Name ) )->checkState() == Qt::Checked )
1167 {
1168 attributes.append( i );
1169 }
1170 }
1171
1172 return attributes;
1173}
1174
1176{
1177 QgsAttributeList attributes;
1178
1179 for ( int i = 0; i < mAttributeTable->rowCount(); i++ )
1180 {
1181 if ( mAttributeTable->item( i, static_cast<int>( ColumnIndex::Name ) )->checkState() == Qt::Checked
1182 && !mAttributeTable->isColumnHidden( static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) )
1183 && mAttributeTable->item( i, static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) )->checkState() == Qt::Checked )
1184 {
1185 attributes.append( i );
1186 }
1187 }
1188
1189 return attributes;
1190}
1191
1193{
1194 QStringList exportNames;
1195 for ( int i = 0; i < mAttributeTable->rowCount(); i++ )
1196 exportNames.append( mAttributeTable->item( i, static_cast<int>( ColumnIndex::ExportName ) )->text() );
1197
1198 return exportNames;
1199}
1200
1202{
1203 return mAddToCanvas->isChecked();
1204}
1205
1207{
1208 mAddToCanvasStateOnOpenCompatibleDriver = enabled;
1209 if ( mAddToCanvas->isEnabled() )
1210 mAddToCanvas->setChecked( enabled );
1211}
1212
1214{
1215 return mSymbologyExportComboBox->currentData().value<Qgis::FeatureSymbologyExport>();
1216}
1217
1219{
1220 return mScaleWidget->scale();
1221}
1222
1224{
1225 mMapCanvas = canvas;
1226 mScaleWidget->setMapCanvas( canvas );
1227 mScaleWidget->setShowCurrentScaleButton( true );
1228 mExtentGroupBox->setCurrentExtent( canvas->mapSettings().visibleExtent(), canvas->mapSettings().destinationCrs() );
1229}
1230
1232{
1233 return mExtentGroupBox->isChecked();
1234}
1235
1237{
1238 return mExtentGroupBox->outputExtent();
1239}
1240
1242{
1243 mSelectedOnly->setChecked( onlySelected );
1244}
1245
1247{
1248 return mSelectedOnly->isChecked();
1249}
1250
1252{
1253 return mCheckPersistMetadata->isChecked();
1254}
1255
1257{
1258 int currentIndexData = mGeometryTypeComboBox->currentData().toInt();
1259 if ( currentIndexData == -1 )
1260 {
1261 //automatic
1263 }
1264
1265 return static_cast<Qgis::WkbType>( currentIndexData );
1266}
1267
1269{
1270 int currentIndexData = mGeometryTypeComboBox->currentData().toInt();
1271 return currentIndexData == -1;
1272}
1273
1275{
1276 return mForceMultiCheckBox->isChecked();
1277}
1278
1280{
1281 mForceMultiCheckBox->setChecked( checked );
1282}
1283
1285{
1286 return mIncludeZCheckBox->isChecked();
1287}
1288
1293
1295{
1296 mIncludeZCheckBox->setChecked( checked );
1297}
1298
1299void QgsVectorLayerSaveAsDialog::mSymbologyExportComboBox_currentIndexChanged( const QString &text )
1300{
1301 bool scaleEnabled = true;
1302 if ( text == tr( "No symbology" ) )
1303 {
1304 scaleEnabled = false;
1305 }
1306 mScaleWidget->setEnabled( scaleEnabled );
1307 mScaleLabel->setEnabled( scaleEnabled );
1308}
1309
1310void QgsVectorLayerSaveAsDialog::mGeometryTypeComboBox_currentIndexChanged( int )
1311{
1312 const int currentIndexData = mGeometryTypeComboBox->currentData().toInt();
1313 if ( currentIndexData != -1 && static_cast<Qgis::WkbType>( currentIndexData ) != Qgis::WkbType::NoGeometry )
1314 {
1315 mForceMultiCheckBox->setEnabled( true );
1316 mIncludeZCheckBox->setEnabled( true );
1317 }
1318 else
1319 {
1320 if ( static_cast<Qgis::WkbType>( currentIndexData ) == Qgis::WkbType::NoGeometry )
1321 {
1322 mForceMultiCheckBox->setEnabled( false );
1323 mForceMultiCheckBox->setChecked( false );
1324 }
1325 else
1326 {
1327 mForceMultiCheckBox->setEnabled( true );
1328 }
1329 mIncludeZCheckBox->setEnabled( false );
1330 mIncludeZCheckBox->setChecked( false );
1331 }
1332}
1333
1334void QgsVectorLayerSaveAsDialog::mSelectAllAttributes_clicked()
1335{
1336 const QSignalBlocker signalBlockerAttributeTable( mAttributeTable );
1337 const QSignalBlocker signalBlockerReplaceRawFieldValues( mReplaceRawFieldValues );
1338
1339 for ( int i = 0; i < mAttributeTable->rowCount(); i++ )
1340 {
1341 if ( mAttributeTable->item( i, static_cast<int>( ColumnIndex::Name ) )->flags() & Qt::ItemIsEnabled )
1342 {
1343 if ( !mAttributeTable->isColumnHidden( static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) )
1344 && ( mAttributeTable->item( i, static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) )->flags() & Qt::ItemIsUserCheckable ) )
1345 {
1346 mAttributeTable->item( i, static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) )->setFlags( Qt::ItemIsUserCheckable | Qt::ItemIsEnabled );
1347 }
1348 mAttributeTable->item( i, static_cast<int>( ColumnIndex::Name ) )->setCheckState( Qt::Checked );
1349 }
1350 }
1351 if ( !mAttributeTable->isColumnHidden( static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) ) )
1352 {
1353 mReplaceRawFieldValues->setEnabled( true );
1354 }
1355}
1356
1357void QgsVectorLayerSaveAsDialog::mDeselectAllAttributes_clicked()
1358{
1359 const QSignalBlocker signalBlockerAttributeTable( mAttributeTable );
1360 const QSignalBlocker signalBlockerReplaceRawFieldValues( mReplaceRawFieldValues );
1361
1362 for ( int i = 0; i < mAttributeTable->rowCount(); i++ )
1363 {
1364 mAttributeTable->item( i, static_cast<int>( ColumnIndex::Name ) )->setCheckState( Qt::Unchecked );
1365 if ( !mAttributeTable->isColumnHidden( static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) )
1366 && ( mAttributeTable->item( i, static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) )->flags() & Qt::ItemIsUserCheckable ) )
1367 {
1368 mAttributeTable->item( i, static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) )->setFlags( Qt::ItemIsUserCheckable );
1369 mAttributeTable->item( i, static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) )->setCheckState( Qt::Unchecked );
1370 }
1371 }
1372 if ( !mAttributeTable->isColumnHidden( static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) ) )
1373 {
1374 mReplaceRawFieldValues->setCheckState( Qt::Unchecked );
1375 mReplaceRawFieldValues->setEnabled( false );
1376 }
1377}
1378
1379void QgsVectorLayerSaveAsDialog::showHelp()
1380{
1381 QgsHelp::openHelp( u"managing_data_source/create_layers.html#creating-new-layers-from-an-existing-layer"_s );
1382}
1383
1384void QgsVectorLayerSaveAsDialog::setCrsForFormat()
1385{
1386 const QString outputFormat = format();
1387
1388 bool force4326 = ( outputFormat == "KML"_L1 || outputFormat == "LIBKML"_L1 || outputFormat == "GPX"_L1 );
1389
1390 // GeoJSON with RFC7946=YES should use EPSG:4326
1391 if ( outputFormat == "GeoJSON"_L1 )
1392 {
1393 QComboBox *cmb = mLayerOptionsGroupBox->findChild<QComboBox *>( u"RFC7946"_s );
1394 if ( cmb && cmb->currentText() == "YES"_L1 )
1395 {
1396 force4326 = true;
1397 }
1398 }
1399
1400 if ( force4326 && !mCrsDefinedByFormat )
1401 {
1402 mUserDefinedCrs = mCrsSelector->crs();
1403 mCrsDefinedByFormat = true;
1404 mCrsSelector->setEnabled( false );
1405 mSelectedCrs = QgsCoordinateReferenceSystem( u"EPSG:4326"_s );
1406 whileBlocking( mCrsSelector )->setCrs( mSelectedCrs );
1407 mExtentGroupBox->setOutputCrs( mSelectedCrs );
1408 }
1409 else if ( !force4326 && mCrsDefinedByFormat )
1410 {
1411 mCrsDefinedByFormat = false;
1412 mCrsSelector->setEnabled( true );
1413 mCrsSelector->setCrs( mUserDefinedCrs );
1414 }
1415}
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:294
@ Point
Point.
Definition qgis.h:296
@ LineString
LineString.
Definition qgis.h:297
@ Polygon
Polygon.
Definition qgis.h:298
@ NoGeometry
No geometry.
Definition qgis.h:312
@ Unknown
Unknown.
Definition qgis.h:295
@ GeometryCollection
GeometryCollection.
Definition qgis.h:303
FeatureSymbologyExport
Options for exporting features considering their symbology.
Definition qgis.h:6228
@ PerFeature
Keeps the number of features and export symbology per feature.
Definition qgis.h:6230
@ PerSymbolLayer
Exports one feature per symbol layer (considering symbol levels).
Definition qgis.h:6231
@ NoSymbology
Export only data.
Definition qgis.h:6229
Represents a coordinate reference system (CRS).
bool isValid() const
Returns true if the datum ensemble is a valid object, or false if it is a null/invalid object.
Definition qgsdatums.h:106
QString name() const
Display name of datum ensemble.
Definition qgsdatums.h:111
QString name() const
Returns the human readable identifier name of this widget type.
QgsEditorWidgetSetup findBest(const QgsVectorLayer *vl, const QString &fieldName) const
Find the best editor widget and its configuration for a given field.
QgsEditorWidgetFactory * factory(const QString &widgetId)
Gets a factory for the given widget type id.
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:56
QMetaType::Type type
Definition qgsfield.h:63
QString typeName() const
Gets the field type.
Definition qgsfield.cpp:158
QString name
Definition qgsfield.h:65
QString displayName() const
Returns the name to use when displaying this field.
Definition qgsfield.cpp:96
@ SaveFile
Select a single new or pre-existing file.
void fileChanged(const QString &path)
Emitted whenever the current file or directory path is changed.
@ ClearToDefault
Reset value to default value (see defaultValue() ).
static QgsEditorWidgetRegistry * editorWidgetRegistry()
Returns the global editor widget registry, used for managing all known edit widget factories.
Definition qgsgui.cpp:109
static void enableAutoGeometryRestore(QWidget *widget, const QString &key=QString())
Register the widget to allow its position to be automatically saved and restored when open and closed...
Definition qgsgui.cpp:224
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
Definition qgshelp.cpp:41
static QIcon iconForWkbType(Qgis::WkbType type)
Returns the icon for a vector layer whose geometry type is provided.
Map canvas is a class for displaying all GIS data types on a canvas.
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
static QString launderLayerName(const QString &name)
Launders a layer's name, converting it into a format which is general suitable for file names or data...
QString name
Definition qgsmaplayer.h:87
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:90
QgsRectangle visibleExtent() const
Returns the actual extent derived from requested extent that takes output image size into account.
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system for the map render.
void crsChanged(const QgsCoordinateReferenceSystem &crs)
Emitted when the selected CRS is changed.
QList< QgsProviderSublayerDetails > querySublayers(const QString &uri, Qgis::SublayerQueryFlags flags=Qgis::SublayerQueryFlags(), QgsFeedback *feedback=nullptr) const
Queries the specified uri and returns a list of any valid sublayers found in the dataset which can be...
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
Contains details about a sub layer available from a dataset.
A rectangle specified with double values.
Stores settings for use within QGIS.
Definition qgssettings.h:68
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
static QVariant createNullVariant(QMetaType::Type metaType)
Helper method to properly create a null QVariant from a metaType Returns the created QVariant.
static QStringList availableEncodings()
Returns a list of available encodings.
A hidden option for file writing for a particular output format.
An available option for configuring file writing for a particular output format, presenting an intege...
QgsVectorFileWriter::OptionType type
An available option for configuring file writing for a particular output format, presenting a choice ...
An available option for configuring file writing for a particular output format, presenting a freefor...
A convenience class for writing vector layers to disk based formats (e.g.
@ CanAppendToExistingLayer
Flag to indicate that new features can be added to an existing layer.
@ CanAddNewLayer
Flag to indicate that a new layer can be added to the dataset.
@ CanDeleteLayer
Flag to indicate that an existing layer can be deleted.
static QgsVectorFileWriter::EditionCapabilities editionCapabilities(const QString &datasetName)
Returns edition capabilities for an existing dataset name.
static bool supportsFeatureStyles(const QString &driverName)
Returns true if the specified driverName supports feature styles.
QFlags< EditionCapability > EditionCapabilities
Combination of CanAddNewLayer, CanAppendToExistingLayer, CanAddNewFieldsToExistingLayer or CanDeleteL...
static bool targetLayerExists(const QString &datasetName, const QString &layerName)
Returns whether the target layer already exists.
static bool driverMetadata(const QString &driverName, MetaData &driverMetadata)
static QString filterForDriver(const QString &driverName)
Creates a filter for an OGR driver key.
static bool areThereNewFieldsToCreate(const QString &datasetName, const QString &layerName, QgsVectorLayer *layer, const QgsAttributeList &attributes)
Returns whether there are among the attributes specified some that do not exist yet in the layer.
static QList< QgsVectorFileWriter::DriverDetails > ogrDriverList(VectorFormatOptions options=SortRecommended)
Returns the driver list that can be used for dialogs.
ActionOnExistingFile
Enumeration to describe how to handle existing files.
@ CreateOrOverwriteLayer
Create or overwrite layer.
@ CreateOrOverwriteFile
Create or overwrite file.
@ AppendToLayerNoNewFields
Append features to existing layer, but do not create new fields.
@ AppendToLayerAddFields
Append features to existing layer, and create new fields if needed.
bool onlySelected() const
Returns whether only selected features will be saved.
bool forceMulti() const
Returns true if force multi geometry type is checked.
QgsAttributeList selectedAttributes() const
Returns a list of attributes which are selected for saving.
QgsRectangle filterExtent() const
Determines the extent to be exported.
QString format() const
Returns the selected format in which the export should be written.
QStringList datasourceOptions() const
Returns a list of additional data source options which are passed to OGR.
bool persistMetadata() const
Returns true if the persist metadata (copy source metadata to destination layer) option is checked.
QString encoding() const
Returns the selected encoding for the target file.
void setIncludeZ(bool checked)
Sets whether the include z dimension checkbox should be checked.
QStringList attributesExportNames() const
Returns a list of export names for attributes.
QString fileName() const
Returns the target filename.
void setOnlySelected(bool onlySelected)
Sets whether only selected features will be saved.
QString layerName() const
Returns the target layer name.
bool automaticGeometryType() const
Returns true if geometry type is set to automatic.
Q_DECL_DEPRECATED QgsVectorLayerSaveAsDialog(long srsid, QWidget *parent=nullptr, Qt::WindowFlags fl=Qt::WindowFlags())
Construct a new QgsVectorLayerSaveAsDialog.
bool includeZ() const
Returns true if include z dimension is checked.
@ DestinationCrs
Show destination CRS (reprojection) option.
@ Fields
Show field customization group.
@ SelectedOnly
Show selected features only option.
QgsCoordinateReferenceSystem crs() const
Returns the CRS chosen for export.
QStringList layerOptions() const
Returns a list of additional layer options which are passed to OGR.
void setForceMulti(bool checked)
Sets whether the force multi geometry checkbox should be checked.
QFlags< Option > Options
Available dialog options.
bool addToCanvas() const
Returns true if the "add to canvas" checkbox is checked.
void setMapCanvas(QgsMapCanvas *canvas)
Sets a map canvas to associate with the dialog.
Qgis::WkbType geometryType() const
Returns the selected flat geometry type for the export.
QgsVectorFileWriter::ActionOnExistingFile creationActionOnExistingFile() const
Returns the creation action.
QgsAttributeList attributesAsDisplayedValues() const
Returns selected attributes that must be exported with their displayed values instead of their raw va...
double scale() const
Returns the specified map scale.
bool hasFilterExtent() const
Determines if filtering the export by an extent is activated.
Qgis::FeatureSymbologyExport symbologyExport() const
Returns type of symbology export.
void setAddToCanvas(bool checked)
Sets whether the "add to canvas" checkbox should be checked.
Represents a vector layer which manages a vector based dataset.
int selectedFeatureCount() const
Returns the number of features that are selected in this layer.
QgsRectangle extent() const final
Returns the extent of the layer.
static Q_INVOKABLE QString translatedDisplayString(Qgis::WkbType type)
Returns a translated display string type for a WKB type, e.g., the geometry name used in WKT geometry...
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition qgis.h:7356
QList< int > QgsAttributeList
Definition qgsfield.h:30
Details of available driver formats.
QMap< QString, QgsVectorFileWriter::Option * > driverOptions
QMap< QString, QgsVectorFileWriter::Option * > layerOptions
QString compulsoryEncoding
Some formats require a compulsory encoding, typically UTF-8. If no compulsory encoding,...