QGIS API Documentation 4.1.0-Master (467af3bbe65)
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->setValue( opt->defaultValue );
259 control = sb;
260 }
261 break;
262 }
263
265 {
266 QgsVectorFileWriter::SetOption *opt = dynamic_cast<QgsVectorFileWriter::SetOption *>( option );
267 if ( opt )
268 {
269 QComboBox *cb = new QComboBox();
270 cb->setObjectName( it.key() );
271 for ( const QString &val : std::as_const( opt->values ) )
272 {
273 cb->addItem( val, val );
274 }
275 if ( opt->allowNone )
276 cb->addItem( tr( "<Default>" ), QgsVariantUtils::createNullVariant( QMetaType::Type::QString ) );
277 int idx = cb->findText( opt->defaultValue );
278 if ( idx == -1 )
279 idx = cb->findData( QgsVariantUtils::createNullVariant( QMetaType::Type::QString ) );
280 cb->setCurrentIndex( idx );
281 control = cb;
282 }
283 break;
284 }
285
287 {
288 QgsVectorFileWriter::StringOption *opt = dynamic_cast<QgsVectorFileWriter::StringOption *>( option );
289 if ( opt )
290 {
291 QLineEdit *le = new QLineEdit( opt->defaultValue );
292 le->setObjectName( it.key() );
293 control = le;
294 }
295 break;
296 }
297
299 control = nullptr;
300 break;
301 }
302
303 if ( control )
304 {
305 QLabel *label = new QLabel( it.key() );
306
307 // Pack the tooltip in some html element, so it gets linebreaks.
308 label->setToolTip( u"<p>%1</p>"_s.arg( option->docString.toHtmlEscaped() ) );
309 control->setToolTip( u"<p>%1</p>"_s.arg( option->docString.toHtmlEscaped() ) );
310
311 controls << QPair<QLabel *, QWidget *>( label, control );
312 }
313 }
314
315 return controls;
316}
317
319{
320#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION( 3, 9, 0 )
321 if ( format() == "OpenFileGDB"_L1 )
322 {
323 // The OpenFileGDB driver supports 64-bit integer fields starting with GDAL 3.9,
324 // if selecting the TARGET_ARCGIS_VERSION=ARCGIS_PRO_3_2_OR_LATER option
325 bool targetAll = true;
326 for ( const QString &layerOption : layerOptions() )
327 {
328 if ( layerOption == "TARGET_ARCGIS_VERSION=ARCGIS_PRO_3_2_OR_LATER"_L1 )
329 {
330 targetAll = false;
331 }
332 }
333 if ( targetAll )
334 {
335 const QgsAttributeList attributesSelected = selectedAttributes();
336 for ( int i = 0; i < attributesSelected.size(); ++i )
337 {
338 QgsField fld = mLayer->fields().at( attributesSelected.at( i ) );
339 if ( fld.type() == QMetaType::Type::LongLong )
340 {
341 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?" ) )
342 != QMessageBox::Yes )
343 {
344 return;
345 }
346 break;
347 }
348 }
349 }
350 }
351 else if ( format() == "FileGDB"_L1 )
352 {
353 // The FileGDB driver based on the ESRI SDK doesn't support 64-bit integers
354 const QgsAttributeList attributesSelected = selectedAttributes();
355 for ( int i = 0; i < attributesSelected.size(); ++i )
356 {
357 QgsField fld = mLayer->fields().at( attributesSelected.at( i ) );
358 if ( fld.type() == QMetaType::Type::LongLong )
359 {
360 if ( QMessageBox::question(
361 this,
362 tr( "Save Vector Layer As" ),
363 tr(
364 "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 "
365 "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?"
366 )
367 .arg( tr( "ESRI File Geodatabase" ) )
368 )
369 != QMessageBox::Yes )
370 {
371 return;
372 }
373 break;
374 }
375 }
376 }
377#endif
378
379 if ( QFile::exists( fileName() ) )
380 {
383 QMessageBox msgBox;
384 msgBox.setIcon( QMessageBox::Question );
385 msgBox.setWindowTitle( tr( "Save Vector Layer As" ) );
386 QPushButton *overwriteFileButton = msgBox.addButton( tr( "Overwrite File" ), QMessageBox::ActionRole );
387 QPushButton *overwriteLayerButton = msgBox.addButton( tr( "Overwrite Layer" ), QMessageBox::ActionRole );
388 QPushButton *appendToLayerButton = msgBox.addButton( tr( "Append to Layer" ), QMessageBox::ActionRole );
389 msgBox.setStandardButtons( QMessageBox::Cancel );
390 msgBox.setDefaultButton( QMessageBox::Cancel );
391 overwriteFileButton->hide();
392 overwriteLayerButton->hide();
393 appendToLayerButton->hide();
394 if ( layerExists )
395 {
397 {
398 msgBox.setText( tr( "The layer already exists. Do you want to overwrite the whole file or overwrite the layer?" ) );
399 overwriteFileButton->setVisible( true );
400 overwriteLayerButton->setVisible( true );
401 }
403 {
404 msgBox.setText( tr( "The file already exists. Do you want to overwrite it?" ) );
405 overwriteFileButton->setVisible( true );
406 }
408 {
409 msgBox.setText( tr( "The layer already exists. Do you want to overwrite the whole file, overwrite the layer or append features to the layer?" ) );
410 appendToLayerButton->setVisible( true );
411 overwriteFileButton->setVisible( true );
412 overwriteLayerButton->setVisible( true );
413 }
414 else
415 {
416 msgBox.setText( tr( "The layer already exists. Do you want to overwrite the whole file or append features to the layer?" ) );
417 appendToLayerButton->setVisible( true );
418 overwriteFileButton->setVisible( true );
419 }
420
421 int ret = msgBox.exec();
422 if ( ret == QMessageBox::Cancel )
423 return;
424 if ( msgBox.clickedButton() == overwriteFileButton )
425 mActionOnExistingFile = QgsVectorFileWriter::CreateOrOverwriteFile;
426 else if ( msgBox.clickedButton() == overwriteLayerButton )
427 mActionOnExistingFile = QgsVectorFileWriter::CreateOrOverwriteLayer;
428 else if ( msgBox.clickedButton() == appendToLayerButton )
429 mActionOnExistingFile = QgsVectorFileWriter::AppendToLayerNoNewFields;
430 }
431 else // !layerExists
432 {
434 {
435 mActionOnExistingFile = QgsVectorFileWriter::CreateOrOverwriteLayer;
436 }
437 else
438 {
439 // should not reach here, layer does not exist and cannot add new layer
440 if ( QMessageBox::question( this, tr( "Save Vector Layer As" ), tr( "The file already exists. Do you want to overwrite it?" ) ) != QMessageBox::Yes )
441 {
442 return;
443 }
444 mActionOnExistingFile = QgsVectorFileWriter::CreateOrOverwriteFile;
445 }
446 }
447 }
448
449 if ( mActionOnExistingFile == QgsVectorFileWriter::AppendToLayerNoNewFields )
450 {
452 {
453 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 )
454 {
455 mActionOnExistingFile = QgsVectorFileWriter::AppendToLayerAddFields;
456 }
457 }
458 }
459 else if ( mActionOnExistingFile == QgsVectorFileWriter::CreateOrOverwriteFile && QFile::exists( fileName() ) )
460 {
461 const QList<QgsProviderSublayerDetails> sublayers = QgsProviderRegistry::instance()->querySublayers( fileName() );
462 QStringList layerList;
463 layerList.reserve( sublayers.size() );
464 for ( const QgsProviderSublayerDetails &sublayer : sublayers )
465 {
466 layerList.append( sublayer.name() );
467 }
468 if ( layerList.length() > 1 )
469 {
470 layerList.sort( Qt::CaseInsensitive );
471 QMessageBox msgBox;
472 msgBox.setIcon( QMessageBox::Warning );
473 msgBox.setWindowTitle( tr( "Overwrite File" ) );
474 msgBox.setText( tr( "This file contains %1 layers that will be lost!\n" ).arg( QLocale().toString( layerList.length() ) ) );
475 msgBox.setDetailedText( tr( "The following layers will be permanently lost:\n\n%1" ).arg( layerList.join( "\n" ) ) );
476 msgBox.setStandardButtons( QMessageBox::Ok | QMessageBox::Cancel );
477 if ( msgBox.exec() == QMessageBox::Cancel )
478 return;
479 }
480 }
481
482 QgsSettings settings;
483 settings.setValue( u"UI/lastVectorFileFilterDir"_s, QFileInfo( fileName() ).absolutePath() );
484 settings.setValue( u"UI/lastVectorFormat"_s, format() );
485 settings.setValue( u"UI/encoding"_s, encoding() );
486 QDialog::accept();
487}
488
489void QgsVectorLayerSaveAsDialog::mFormatComboBox_currentIndexChanged( int idx )
490{
491 Q_UNUSED( idx )
492
493 mFilename->setEnabled( true );
494 QString filter = QgsVectorFileWriter::filterForDriver( format() );
495 // A bit of hack to solve https://github.com/qgis/QGIS/issues/54566
496 // to be able to select an existing File Geodatabase, we add in the filter
497 // the "gdb" file that is found in all File Geodatabase .gdb directory
498 // to allow the user to select it. We need to detect this particular case
499 // in QgsFileWidget::openFileDialog() to remove this gdb file from the
500 // selected filename
501 if ( format() == "OpenFileGDB"_L1 || format() == "FileGDB"_L1 )
502 filter = u"%1 (*.gdb *.GDB gdb)"_s.arg( tr( "ESRI File Geodatabase" ) );
503 mFilename->setFilter( filter );
504
505 // if output filename already defined we need to replace old suffix
506 // to avoid double extensions like .gpkg.shp
507 if ( !mFilename->filePath().isEmpty() )
508 {
509 const thread_local QRegularExpression rx( "\\.(.*?)[\\s]" );
510 const QString ext = rx.match( filter ).captured( 1 );
511 if ( !ext.isEmpty() )
512 {
513 QFileInfo fi( mFilename->filePath() );
514 mFilename->setFilePath( u"%1/%2.%3"_s.arg( fi.path(), fi.baseName(), ext ) );
515 }
516 }
517
518 bool selectAllFields = true;
519
520 // Is it a format for which fields that have attached widgets of types
521 // ValueMap, ValueRelation, etc. should be by default exported with their displayed
522 // values
523 bool isFormatForFieldsAsDisplayedValues = false;
524
525 const QString sFormat( format() );
526 if ( sFormat == "DXF"_L1 || sFormat == "DGN"_L1 )
527 {
528 mAttributesSelection->setVisible( false );
529 selectAllFields = false;
530 }
531 else
532 {
533 if ( mOptions & Option::Fields )
534 {
535 mAttributesSelection->setVisible( true );
536 isFormatForFieldsAsDisplayedValues = ( sFormat == "CSV"_L1 || sFormat == "XLS"_L1 || sFormat == "XLSX"_L1 || sFormat == "ODS"_L1 );
537 }
538 }
539
540 // Show symbology options only for some formats
541 if ( QgsVectorFileWriter::supportsFeatureStyles( sFormat ) && ( mOptions & Option::Symbology ) )
542 {
543 mSymbologyExportLabel->setVisible( true );
544 mSymbologyExportComboBox->setVisible( true );
545 mScaleLabel->setVisible( true );
546 mScaleWidget->setVisible( true );
547 }
548 else
549 {
550 mSymbologyExportLabel->hide();
551 mSymbologyExportComboBox->hide();
552 mScaleLabel->hide();
553 mScaleWidget->hide();
554 }
555
556 leLayername->setEnabled(
557 sFormat == "KML"_L1 || sFormat == "GPKG"_L1 || sFormat == "XLSX"_L1 || sFormat == "ODS"_L1 || sFormat == "FileGDB"_L1 || sFormat == "OpenFileGDB"_L1 || sFormat == "SQLite"_L1 || sFormat == "SpatiaLite"_L1
558 );
559
560 if ( sFormat == "XLSX"_L1 )
561 leLayername->setMaxLength( 31 );
562 else if ( leLayername->isEnabled() )
563 leLayername->setMaxLength( 32767 ); // default length
564
565 if ( !leLayername->isEnabled() )
566 leLayername->setText( QString() );
567 else if ( leLayername->text().isEmpty() )
568 {
569 QString layerName = mDefaultOutputLayerNameFromInputLayerName;
570 if ( layerName.isEmpty() && !mFilename->filePath().isEmpty() )
571 {
572 layerName = QFileInfo( mFilename->filePath() ).baseName();
573 leLayername->setDefaultValue( layerName );
574 }
575 if ( layerName.isEmpty() )
576 layerName = tr( "new_layer" );
577 leLayername->setText( layerName );
578 }
579
580 if ( mLayer )
581 {
582 // save fields state: if it is checked and should be exported as displayed value
583 const bool isPreviousFormatForFieldsAsDisplayedValues = ( mPreviousFormat == "CSV"_L1 || mPreviousFormat == "XLS"_L1 || mPreviousFormat == "XLSX"_L1 || mPreviousFormat == "ODS"_L1 );
584 for ( int i = 0; i < mAttributeTable->rowCount(); i++ )
585 {
586 QTableWidgetItem *nameItem = mAttributeTable->item( i, static_cast<int>( ColumnIndex::Name ) );
587 if ( !nameItem )
588 {
589 continue;
590 }
591
592 const QString fieldName = nameItem->text();
593 const bool exportField = ( nameItem->checkState() == Qt::Checked );
594
595 std::optional<bool> exportAsValue = std::nullopt;
596 if ( isPreviousFormatForFieldsAsDisplayedValues )
597 {
598 QTableWidgetItem *valueItem = mAttributeTable->item( i, static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) );
599 if ( valueItem && ( valueItem->flags() & Qt::ItemIsUserCheckable ) )
600 {
601 exportAsValue = ( valueItem->checkState() == Qt::Checked );
602 }
603 }
604 else
605 {
606 exportAsValue = mFieldsState[fieldName].second;
607 }
608
609 mFieldsState[fieldName] = { exportField, exportAsValue };
610 }
611
612 mPreviousFormat = sFormat;
613
614 mAttributeTable->setRowCount( mLayer->fields().count() );
615
616 QStringList horizontalHeaders = QStringList() << tr( "Name" ) << tr( "Export name" ) << tr( "Type" ) << tr( "Replace with displayed values" );
617 mAttributeTable->setColumnCount( static_cast<int>( horizontalHeaders.size() ) );
618 mAttributeTable->setHorizontalHeaderLabels( horizontalHeaders );
619
620 bool foundFieldThatCanBeExportedAsDisplayedValue = false;
621 for ( int i = 0; i < mLayer->fields().size(); ++i )
622 {
623 const QgsEditorWidgetSetup setup = QgsGui::editorWidgetRegistry()->findBest( mLayer, mLayer->fields()[i].name() );
624 if ( setup.type() != "TextEdit"_L1 && QgsGui::editorWidgetRegistry()->factory( setup.type() ) )
625 {
626 foundFieldThatCanBeExportedAsDisplayedValue = true;
627 break;
628 }
629 }
630 mAttributeTable->setColumnHidden( static_cast<int>( ColumnIndex::ExportAsDisplayedValue ), !foundFieldThatCanBeExportedAsDisplayedValue );
631
632 bool allChecked = true;
633 bool allUnchecked = true;
634 bool anyEnabled = false;
635
636 const QSignalBlocker signalBlockerAttributeTable( mAttributeTable );
637 {
638 for ( int i = 0; i < mLayer->fields().size(); ++i )
639 {
640 QgsField fld = mLayer->fields().at( i );
641 Qt::ItemFlags flags = mLayer->providerType() != "oracle"_L1 || !fld.typeName().contains( "SDO_GEOMETRY"_L1 ) ? Qt::ItemIsEnabled : Qt::NoItemFlags;
642 QTableWidgetItem *item = nullptr;
643 const QString fieldName = fld.name();
644 const bool exportField = mFieldsState.contains( fieldName ) ? mFieldsState[fieldName].first : selectAllFields;
645 item = new QTableWidgetItem( fieldName );
646 item->setFlags( flags | Qt::ItemIsUserCheckable );
647 item->setCheckState( ( exportField ) ? Qt::Checked : Qt::Unchecked );
648 mAttributeTable->setItem( i, static_cast<int>( ColumnIndex::Name ), item );
649
650 item = new QTableWidgetItem( fieldName );
651 item->setFlags( flags | Qt::ItemIsEditable );
652 item->setData( Qt::UserRole, fld.displayName() );
653 mAttributeTable->setItem( i, static_cast<int>( ColumnIndex::ExportName ), item );
654
655 item = new QTableWidgetItem( fld.typeName() );
656 item->setFlags( flags );
657 mAttributeTable->setItem( i, static_cast<int>( ColumnIndex::Type ), item );
658
659 if ( foundFieldThatCanBeExportedAsDisplayedValue )
660 {
661 const QgsEditorWidgetSetup setup = QgsGui::editorWidgetRegistry()->findBest( mLayer, mLayer->fields()[i].name() );
662 QgsEditorWidgetFactory *factory = nullptr;
663 const QString widgetId( setup.type() );
664 if ( flags == Qt::ItemIsEnabled && widgetId != "TextEdit"_L1 && ( factory = QgsGui::editorWidgetRegistry()->factory( widgetId ) ) )
665 {
666 item = new QTableWidgetItem( tr( "Use %1" ).arg( factory->name() ) );
667 bool exportAsValue;
668 if ( mFieldsState.contains( fieldName ) && mFieldsState[fieldName].second.has_value() )
669 {
670 exportAsValue = mFieldsState[fieldName].second.value();
671 }
672 else
673 {
674 exportAsValue
675 = ( exportField && isFormatForFieldsAsDisplayedValues && ( widgetId == "ValueMap"_L1 || widgetId == "ValueRelation"_L1 || widgetId == "CheckBox"_L1 || widgetId == "RelationReference"_L1 ) );
676 }
677 item->setFlags( ( exportField ) ? ( Qt::ItemIsEnabled | Qt::ItemIsUserCheckable ) : Qt::ItemIsUserCheckable );
678 item->setCheckState( exportAsValue ? Qt::Checked : Qt::Unchecked );
679 mAttributeTable->setItem( i, static_cast<int>( ColumnIndex::ExportAsDisplayedValue ), item );
680
681 // collect information for setting mReplaceRawFieldValues state
682 if ( exportField )
683 {
684 anyEnabled = true;
685 if ( exportAsValue )
686 allUnchecked = false;
687 else
688 allChecked = false;
689 }
690 }
691 else
692 {
693 item = new QTableWidgetItem();
694 item->setFlags( Qt::NoItemFlags );
695 mAttributeTable->setItem( i, static_cast<int>( ColumnIndex::ExportAsDisplayedValue ), item );
696 }
697 }
698 }
699 }
700
701 Qt::CheckState replaceRawState;
702 if ( !anyEnabled || allUnchecked )
703 replaceRawState = Qt::Unchecked;
704 else if ( allChecked )
705 replaceRawState = Qt::Checked;
706 else
707 replaceRawState = Qt::PartiallyChecked;
708
709 whileBlocking( mReplaceRawFieldValues )->setCheckState( replaceRawState );
710 mReplaceRawFieldValues->setEnabled( selectAllFields && anyEnabled );
711 mReplaceRawFieldValues->setVisible( foundFieldThatCanBeExportedAsDisplayedValue );
712
713 mAttributeTable->resizeColumnsToContents();
714 }
715
716 QgsVectorFileWriter::MetaData driverMetaData;
717
718 while ( mDatasourceOptionsGroupBox->layout()->count() )
719 {
720 QLayoutItem *item = mDatasourceOptionsGroupBox->layout()->takeAt( 0 );
721 delete item->widget();
722 delete item;
723 }
724
725 while ( mLayerOptionsGroupBox->layout()->count() )
726 {
727 QLayoutItem *item = mLayerOptionsGroupBox->layout()->takeAt( 0 );
728 delete item->widget();
729 delete item;
730 }
731
732 typedef QPair<QLabel *, QWidget *> LabelControlPair;
733
734 if ( QgsVectorFileWriter::driverMetadata( sFormat, driverMetaData ) )
735 {
736 if ( !driverMetaData.driverOptions.empty() )
737 {
738 mDatasourceOptionsGroupBox->setVisible( true );
739 QList<QPair<QLabel *, QWidget *>> controls = createControls( driverMetaData.driverOptions );
740
741 QFormLayout *datasourceLayout = dynamic_cast<QFormLayout *>( mDatasourceOptionsGroupBox->layout() );
742
743 const auto constControls = controls;
744 for ( LabelControlPair control : constControls )
745 {
746 datasourceLayout->addRow( control.first, control.second );
747 }
748 }
749 else
750 {
751 mDatasourceOptionsGroupBox->setVisible( false );
752 }
753
754 if ( !driverMetaData.layerOptions.empty() )
755 {
756 mLayerOptionsGroupBox->setVisible( true );
757 QList<QPair<QLabel *, QWidget *>> controls = createControls( driverMetaData.layerOptions );
758
759 QFormLayout *layerOptionsLayout = dynamic_cast<QFormLayout *>( mLayerOptionsGroupBox->layout() );
760
761 const auto constControls = controls;
762 for ( LabelControlPair control : constControls )
763 {
764 layerOptionsLayout->addRow( control.first, control.second );
765 }
766
767 // for GeoJSON we need to track changes of the RFC7946 option to update CRS accordingly
768 if ( sFormat == "GeoJSON"_L1 )
769 {
770 if ( QComboBox *cmbRfc7946 = mLayerOptionsGroupBox->findChild<QComboBox *>( "RFC7946"_L1 ) )
771 {
772 connect( cmbRfc7946, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsVectorLayerSaveAsDialog::setCrsForFormat );
773 }
774 }
775 }
776 else
777 {
778 mLayerOptionsGroupBox->setVisible( false );
779 }
780
781 if ( driverMetaData.compulsoryEncoding.isEmpty() )
782 {
783 mEncodingComboBox->setEnabled( true );
784 }
785 else
786 {
787 int idx = mEncodingComboBox->findText( driverMetaData.compulsoryEncoding );
788 if ( idx >= 0 )
789 {
790 mEncodingComboBox->setCurrentIndex( idx );
791 mEncodingComboBox->setDisabled( true );
792 }
793 else
794 {
795 mEncodingComboBox->setEnabled( true );
796 }
797 }
798 }
799 else
800 {
801 mEncodingComboBox->setEnabled( true );
802 }
803
804 GDALDriverH hDriver = GDALGetDriverByName( format().toUtf8().constData() );
805 if ( hDriver )
806 {
807 const bool canReopen = GDALGetMetadataItem( hDriver, GDAL_DCAP_OPEN, nullptr );
808 if ( mAddToCanvas->isEnabled() && !canReopen )
809 {
810 mAddToCanvasStateOnOpenCompatibleDriver = mAddToCanvas->isChecked();
811 mAddToCanvas->setChecked( false );
812 mAddToCanvas->setEnabled( false );
813 }
814 else if ( !mAddToCanvas->isEnabled() && canReopen )
815 {
816 mAddToCanvas->setChecked( mAddToCanvasStateOnOpenCompatibleDriver );
817 mAddToCanvas->setEnabled( true );
818 }
819 }
820
821 // update CRS selector based on the selected format and layer creation options
822 setCrsForFormat();
823}
824
825void QgsVectorLayerSaveAsDialog::mUseAliasesForExportedName_stateChanged( int state )
826{
827 const QSignalBlocker signalBlocker( mAttributeTable );
828
829 switch ( state )
830 {
831 case Qt::Unchecked:
832 {
833 // Check for modified entries
834 bool modifiedEntries = false;
835 for ( int i = 0; i < mAttributeTable->rowCount(); i++ )
836 {
837 if ( mAttributeTable->item( i, static_cast<int>( ColumnIndex::ExportName ) )->text() != mAttributeTable->item( i, static_cast<int>( ColumnIndex::ExportName ) )->data( Qt::UserRole ).toString() )
838 {
839 modifiedEntries = true;
840 break;
841 }
842 }
843
844 if ( modifiedEntries )
845 {
846 if ( QMessageBox::question( this, tr( "Modified names" ), tr( "Some names were modified and will be overridden. Do you want to continue?" ) ) == QMessageBox::No )
847 {
848 whileBlocking( mUseAliasesForExportedName )->setCheckState( Qt::PartiallyChecked );
849 return;
850 }
851 }
852
853 for ( int i = 0; i < mAttributeTable->rowCount(); i++ )
854 {
855 mUseAliasesForExportedName->setTristate( false );
856 mAttributeTable->item( i, static_cast<int>( ColumnIndex::ExportName ) )->setText( mAttributeTable->item( i, static_cast<int>( ColumnIndex::Name ) )->text() );
857 }
858 }
859 break;
860 case Qt::Checked:
861 {
862 // Check for modified entries
863 bool modifiedEntries = false;
864 for ( int i = 0; i < mAttributeTable->rowCount(); i++ )
865 {
866 if ( mAttributeTable->item( i, static_cast<int>( ColumnIndex::ExportName ) )->text() != mAttributeTable->item( i, static_cast<int>( ColumnIndex::Name ) )->text() )
867 modifiedEntries = true;
868 }
869
870 if ( modifiedEntries )
871 {
872 if ( QMessageBox::question( this, tr( "Modified names" ), tr( "Some names were modified and will be overridden. Do you want to continue?" ) ) == QMessageBox::No )
873 {
874 whileBlocking( mUseAliasesForExportedName )->setCheckState( Qt::PartiallyChecked );
875 return;
876 }
877 }
878
879 for ( int i = 0; i < mAttributeTable->rowCount(); i++ )
880 {
881 mUseAliasesForExportedName->setTristate( false );
882 const QString alias = mAttributeTable->item( i, static_cast<int>( ColumnIndex::ExportName ) )->data( Qt::UserRole ).toString();
883 mAttributeTable->item( i, static_cast<int>( ColumnIndex::ExportName ) )->setText( alias );
884 }
885 }
886 break;
887 case Qt::PartiallyChecked:
888 // Do nothing
889 break;
890 }
891}
892
893void QgsVectorLayerSaveAsDialog::mReplaceRawFieldValues_stateChanged( int )
894{
895 if ( mAttributeTable->isColumnHidden( static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) ) )
896 return;
897
898 const QSignalBlocker signalBlockerAttributeTable( mAttributeTable );
899 const QSignalBlocker signalBlockerReplaceRawFieldValues( mReplaceRawFieldValues );
900
901 if ( mReplaceRawFieldValues->checkState() != Qt::PartiallyChecked )
902 {
903 for ( int i = 0; i < mAttributeTable->rowCount(); i++ )
904 {
905 if ( mAttributeTable->item( i, static_cast<int>( ColumnIndex::Name ) )->checkState() == Qt::Checked
906 && mAttributeTable->item( i, static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) )
907 && mAttributeTable->item( i, static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) )->flags() & Qt::ItemIsEnabled )
908 {
909 mAttributeTable->item( i, static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) )->setCheckState( mReplaceRawFieldValues->checkState() );
910 }
911 }
912 }
913 mReplaceRawFieldValues->setTristate( false );
914}
915
916void QgsVectorLayerSaveAsDialog::mAttributeTable_itemChanged( QTableWidgetItem *item )
917{
918 const QSignalBlocker signalBlockerAttributeTable( mAttributeTable );
919 const QSignalBlocker signalBlockerReplaceRawFieldValues( mReplaceRawFieldValues );
920
921 int row = item->row();
922 int column = item->column();
923
924 switch ( static_cast<ColumnIndex>( column ) )
925 {
926 case ColumnIndex::Name:
927 {
928 if ( mAttributeTable->isColumnHidden( static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) )
929 || !mAttributeTable->item( row, static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) )
930 || !( mAttributeTable->item( row, static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) )->flags() & Qt::ItemIsUserCheckable ) )
931 return;
932
933 if ( mAttributeTable->item( row, column )->checkState() == Qt::Unchecked )
934 {
935 mAttributeTable->item( row, static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) )->setCheckState( Qt::Unchecked );
936 mAttributeTable->item( row, static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) )->setFlags( Qt::ItemIsUserCheckable );
937 bool checkBoxEnabled = false;
938 for ( int i = 0; i < mAttributeTable->rowCount(); i++ )
939 {
940 if ( mAttributeTable->item( i, static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) )
941 && mAttributeTable->item( i, static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) )->flags() & Qt::ItemIsEnabled )
942 {
943 checkBoxEnabled = true;
944 break;
945 }
946 }
947 mReplaceRawFieldValues->setEnabled( checkBoxEnabled );
948 if ( !checkBoxEnabled )
949 mReplaceRawFieldValues->setCheckState( Qt::Unchecked );
950 }
951 else if ( mAttributeTable->item( row, column )->checkState() == Qt::Checked )
952 {
953 mAttributeTable->item( row, static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) )->setFlags( Qt::ItemIsUserCheckable | Qt::ItemIsEnabled );
954 mReplaceRawFieldValues->setEnabled( true );
955 }
956 }
957 break;
958 case ColumnIndex::ExportName:
959 {
960 // Check empty export name
961 if ( item->text().isEmpty() )
962 {
963 QMessageBox::warning( this, tr( "Empty export name" ), tr( "Empty export name are not allowed." ) );
964 return;
965 }
966
967 // Rename eventually duplicated names
968 QStringList names = attributesExportNames();
969 while ( names.count( item->text() ) > 1 )
970 item->setText( QString( "%1_2" ).arg( item->text() ) );
971
972 mUseAliasesForExportedName->setCheckState( Qt::PartiallyChecked );
973 }
974 break;
975 case ColumnIndex::Type:
976 // Nothing to do
977 break;
978 case ColumnIndex::ExportAsDisplayedValue:
979 {
980 if ( mAttributeTable->item( row, column )->flags() & Qt::ItemIsUserCheckable )
981 {
982 bool allChecked = true;
983 bool allUnchecked = true;
984 for ( int i = 0; i < mAttributeTable->rowCount(); i++ )
985 {
986 if ( mAttributeTable->item( i, static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) )
987 && mAttributeTable->item( i, static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) )->flags() & Qt::ItemIsEnabled )
988 {
989 if ( mAttributeTable->item( i, static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) )->checkState() == Qt::Unchecked )
990 allChecked = false;
991 else
992 allUnchecked = false;
993 }
994 }
995 mReplaceRawFieldValues->setCheckState( ( !allChecked && !allUnchecked ) ? Qt::PartiallyChecked : ( allChecked ) ? Qt::Checked : Qt::Unchecked );
996 }
997 }
998 break;
999 }
1000}
1001
1002void QgsVectorLayerSaveAsDialog::mCrsSelector_crsChanged( const QgsCoordinateReferenceSystem &crs )
1003{
1004 if ( mCrsDefinedByFormat )
1005 {
1006 // this should never happen as CRS selector should be disabled, but let's be safe and
1007 // avoid overwriting user defined CRS with the CRS required by the output format
1008 return;
1009 }
1010
1011 mSelectedCrs = crs;
1012 mUserDefinedCrs = crs;
1013 mExtentGroupBox->setOutputCrs( mSelectedCrs );
1014}
1015
1017{
1018 return mFilename->filePath();
1019}
1020
1022{
1023 return leLayername->text();
1024}
1025
1027{
1028 return mEncodingComboBox->currentText();
1029}
1030
1032{
1033 return mFormatComboBox->currentData().toString();
1034}
1035
1037{
1038 return mSelectedCrs;
1039}
1040
1042{
1043 QStringList options;
1044
1045 QgsVectorFileWriter::MetaData driverMetaData;
1046
1047 if ( QgsVectorFileWriter::driverMetadata( format(), driverMetaData ) )
1048 {
1049 QMap<QString, QgsVectorFileWriter::Option *>::ConstIterator it;
1050
1051 for ( it = driverMetaData.driverOptions.constBegin(); it != driverMetaData.driverOptions.constEnd(); ++it )
1052 {
1053 switch ( it.value()->type )
1054 {
1056 {
1058 QSpinBox *sb = mDatasourceOptionsGroupBox->findChild<QSpinBox *>( it.key() );
1059 if ( opt && sb && sb->value() != opt->defaultValue )
1060 options << u"%1=%2"_s.arg( it.key() ).arg( sb->value() );
1061 break;
1062 }
1063
1065 {
1067 QComboBox *cb = mDatasourceOptionsGroupBox->findChild<QComboBox *>( it.key() );
1068 if ( opt && cb && cb->itemData( cb->currentIndex() ) != opt->defaultValue )
1069 options << u"%1=%2"_s.arg( it.key(), cb->currentText() );
1070 break;
1071 }
1072
1074 {
1076 QLineEdit *le = mDatasourceOptionsGroupBox->findChild<QLineEdit *>( it.key() );
1077 if ( opt && le && le->text() != opt->defaultValue )
1078 options << u"%1=%2"_s.arg( it.key(), le->text() );
1079 break;
1080 }
1081
1083 {
1084 QgsVectorFileWriter::HiddenOption *opt = dynamic_cast<QgsVectorFileWriter::HiddenOption *>( it.value() );
1085 if ( opt && !opt->mValue.isEmpty() )
1086 options << u"%1=%2"_s.arg( it.key(), opt->mValue );
1087 break;
1088 }
1089 }
1090 }
1091 }
1092
1093 QString plainText = mOgrDatasourceOptions->toPlainText().trimmed();
1094 if ( !plainText.isEmpty() )
1095 options += plainText.split( '\n' );
1096
1097 return options;
1098}
1099
1101{
1102 QStringList options;
1103
1104 QgsVectorFileWriter::MetaData driverMetaData;
1105
1106 if ( QgsVectorFileWriter::driverMetadata( format(), driverMetaData ) )
1107 {
1108 QMap<QString, QgsVectorFileWriter::Option *>::ConstIterator it;
1109
1110 for ( it = driverMetaData.layerOptions.constBegin(); it != driverMetaData.layerOptions.constEnd(); ++it )
1111 {
1112 switch ( it.value()->type )
1113 {
1115 {
1116 QgsVectorFileWriter::IntOption *opt = qgis::down_cast<QgsVectorFileWriter::IntOption *>( *it );
1117 QSpinBox *sb = mLayerOptionsGroupBox->findChild<QSpinBox *>( it.key() );
1118 if ( opt && sb && sb->value() != opt->defaultValue )
1119 options << u"%1=%2"_s.arg( it.key() ).arg( sb->value() );
1120 break;
1121 }
1122
1124 {
1125 QgsVectorFileWriter::SetOption *opt = qgis::down_cast<QgsVectorFileWriter::SetOption *>( *it );
1126 QComboBox *cb = mLayerOptionsGroupBox->findChild<QComboBox *>( it.key() );
1127 if ( opt && cb && cb->itemData( cb->currentIndex() ) != opt->defaultValue )
1128 options << u"%1=%2"_s.arg( it.key(), cb->currentText() );
1129 break;
1130 }
1131
1133 {
1134 QgsVectorFileWriter::StringOption *opt = qgis::down_cast<QgsVectorFileWriter::StringOption *>( *it );
1135 QLineEdit *le = mLayerOptionsGroupBox->findChild<QLineEdit *>( it.key() );
1136 if ( opt && le && le->text() != opt->defaultValue )
1137 options << u"%1=%2"_s.arg( it.key(), le->text() );
1138 break;
1139 }
1140
1142 {
1143 QgsVectorFileWriter::HiddenOption *opt = qgis::down_cast<QgsVectorFileWriter::HiddenOption *>( it.value() );
1144 if ( !opt->mValue.isEmpty() )
1145 options << u"%1=%2"_s.arg( it.key(), opt->mValue );
1146 break;
1147 }
1148 }
1149 }
1150 }
1151
1152 QString plainText = mOgrLayerOptions->toPlainText().trimmed();
1153 if ( !plainText.isEmpty() )
1154 options += plainText.split( '\n' );
1155
1156 return options;
1157}
1158
1160{
1161 QgsAttributeList attributes;
1162
1163 for ( int i = 0; i < mAttributeTable->rowCount(); i++ )
1164 {
1165 if ( mAttributeTable->item( i, static_cast<int>( ColumnIndex::Name ) )->checkState() == Qt::Checked )
1166 {
1167 attributes.append( i );
1168 }
1169 }
1170
1171 return attributes;
1172}
1173
1175{
1176 QgsAttributeList attributes;
1177
1178 for ( int i = 0; i < mAttributeTable->rowCount(); i++ )
1179 {
1180 if ( mAttributeTable->item( i, static_cast<int>( ColumnIndex::Name ) )->checkState() == Qt::Checked
1181 && !mAttributeTable->isColumnHidden( static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) )
1182 && mAttributeTable->item( i, static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) )->checkState() == Qt::Checked )
1183 {
1184 attributes.append( i );
1185 }
1186 }
1187
1188 return attributes;
1189}
1190
1192{
1193 QStringList exportNames;
1194 for ( int i = 0; i < mAttributeTable->rowCount(); i++ )
1195 exportNames.append( mAttributeTable->item( i, static_cast<int>( ColumnIndex::ExportName ) )->text() );
1196
1197 return exportNames;
1198}
1199
1201{
1202 return mAddToCanvas->isChecked();
1203}
1204
1206{
1207 mAddToCanvasStateOnOpenCompatibleDriver = enabled;
1208 if ( mAddToCanvas->isEnabled() )
1209 mAddToCanvas->setChecked( enabled );
1210}
1211
1213{
1214 return mSymbologyExportComboBox->currentData().value<Qgis::FeatureSymbologyExport>();
1215}
1216
1218{
1219 return mScaleWidget->scale();
1220}
1221
1223{
1224 mMapCanvas = canvas;
1225 mScaleWidget->setMapCanvas( canvas );
1226 mScaleWidget->setShowCurrentScaleButton( true );
1227 mExtentGroupBox->setCurrentExtent( canvas->mapSettings().visibleExtent(), canvas->mapSettings().destinationCrs() );
1228}
1229
1231{
1232 return mExtentGroupBox->isChecked();
1233}
1234
1236{
1237 return mExtentGroupBox->outputExtent();
1238}
1239
1241{
1242 mSelectedOnly->setChecked( onlySelected );
1243}
1244
1246{
1247 return mSelectedOnly->isChecked();
1248}
1249
1251{
1252 return mCheckPersistMetadata->isChecked();
1253}
1254
1256{
1257 int currentIndexData = mGeometryTypeComboBox->currentData().toInt();
1258 if ( currentIndexData == -1 )
1259 {
1260 //automatic
1262 }
1263
1264 return static_cast<Qgis::WkbType>( currentIndexData );
1265}
1266
1268{
1269 int currentIndexData = mGeometryTypeComboBox->currentData().toInt();
1270 return currentIndexData == -1;
1271}
1272
1274{
1275 return mForceMultiCheckBox->isChecked();
1276}
1277
1279{
1280 mForceMultiCheckBox->setChecked( checked );
1281}
1282
1284{
1285 return mIncludeZCheckBox->isChecked();
1286}
1287
1292
1294{
1295 mIncludeZCheckBox->setChecked( checked );
1296}
1297
1298void QgsVectorLayerSaveAsDialog::mSymbologyExportComboBox_currentIndexChanged( const QString &text )
1299{
1300 bool scaleEnabled = true;
1301 if ( text == tr( "No symbology" ) )
1302 {
1303 scaleEnabled = false;
1304 }
1305 mScaleWidget->setEnabled( scaleEnabled );
1306 mScaleLabel->setEnabled( scaleEnabled );
1307}
1308
1309void QgsVectorLayerSaveAsDialog::mGeometryTypeComboBox_currentIndexChanged( int )
1310{
1311 const int currentIndexData = mGeometryTypeComboBox->currentData().toInt();
1312 if ( currentIndexData != -1 && static_cast<Qgis::WkbType>( currentIndexData ) != Qgis::WkbType::NoGeometry )
1313 {
1314 mForceMultiCheckBox->setEnabled( true );
1315 mIncludeZCheckBox->setEnabled( true );
1316 }
1317 else
1318 {
1319 if ( static_cast<Qgis::WkbType>( currentIndexData ) == Qgis::WkbType::NoGeometry )
1320 {
1321 mForceMultiCheckBox->setEnabled( false );
1322 mForceMultiCheckBox->setChecked( false );
1323 }
1324 else
1325 {
1326 mForceMultiCheckBox->setEnabled( true );
1327 }
1328 mIncludeZCheckBox->setEnabled( false );
1329 mIncludeZCheckBox->setChecked( false );
1330 }
1331}
1332
1333void QgsVectorLayerSaveAsDialog::mSelectAllAttributes_clicked()
1334{
1335 const QSignalBlocker signalBlockerAttributeTable( mAttributeTable );
1336 const QSignalBlocker signalBlockerReplaceRawFieldValues( mReplaceRawFieldValues );
1337
1338 for ( int i = 0; i < mAttributeTable->rowCount(); i++ )
1339 {
1340 if ( mAttributeTable->item( i, static_cast<int>( ColumnIndex::Name ) )->flags() & Qt::ItemIsEnabled )
1341 {
1342 if ( !mAttributeTable->isColumnHidden( static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) )
1343 && ( mAttributeTable->item( i, static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) )->flags() & Qt::ItemIsUserCheckable ) )
1344 {
1345 mAttributeTable->item( i, static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) )->setFlags( Qt::ItemIsUserCheckable | Qt::ItemIsEnabled );
1346 }
1347 mAttributeTable->item( i, static_cast<int>( ColumnIndex::Name ) )->setCheckState( Qt::Checked );
1348 }
1349 }
1350 if ( !mAttributeTable->isColumnHidden( static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) ) )
1351 {
1352 mReplaceRawFieldValues->setEnabled( true );
1353 }
1354}
1355
1356void QgsVectorLayerSaveAsDialog::mDeselectAllAttributes_clicked()
1357{
1358 const QSignalBlocker signalBlockerAttributeTable( mAttributeTable );
1359 const QSignalBlocker signalBlockerReplaceRawFieldValues( mReplaceRawFieldValues );
1360
1361 for ( int i = 0; i < mAttributeTable->rowCount(); i++ )
1362 {
1363 mAttributeTable->item( i, static_cast<int>( ColumnIndex::Name ) )->setCheckState( Qt::Unchecked );
1364 if ( !mAttributeTable->isColumnHidden( static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) )
1365 && ( mAttributeTable->item( i, static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) )->flags() & Qt::ItemIsUserCheckable ) )
1366 {
1367 mAttributeTable->item( i, static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) )->setFlags( Qt::ItemIsUserCheckable );
1368 mAttributeTable->item( i, static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) )->setCheckState( Qt::Unchecked );
1369 }
1370 }
1371 if ( !mAttributeTable->isColumnHidden( static_cast<int>( ColumnIndex::ExportAsDisplayedValue ) ) )
1372 {
1373 mReplaceRawFieldValues->setCheckState( Qt::Unchecked );
1374 mReplaceRawFieldValues->setEnabled( false );
1375 }
1376}
1377
1378void QgsVectorLayerSaveAsDialog::showHelp()
1379{
1380 QgsHelp::openHelp( u"managing_data_source/create_layers.html#creating-new-layers-from-an-existing-layer"_s );
1381}
1382
1383void QgsVectorLayerSaveAsDialog::setCrsForFormat()
1384{
1385 const QString outputFormat = format();
1386
1387 bool force4326 = ( outputFormat == "KML"_L1 || outputFormat == "LIBKML"_L1 || outputFormat == "GPX"_L1 );
1388
1389 // GeoJSON with RFC7946=YES should use EPSG:4326
1390 if ( outputFormat == "GeoJSON"_L1 )
1391 {
1392 QComboBox *cmb = mLayerOptionsGroupBox->findChild<QComboBox *>( u"RFC7946"_s );
1393 if ( cmb && cmb->currentText() == "YES"_L1 )
1394 {
1395 force4326 = true;
1396 }
1397 }
1398
1399 if ( force4326 && !mCrsDefinedByFormat )
1400 {
1401 mUserDefinedCrs = mCrsSelector->crs();
1402 mCrsDefinedByFormat = true;
1403 mCrsSelector->setEnabled( false );
1404 mSelectedCrs = QgsCoordinateReferenceSystem( u"EPSG:4326"_s );
1405 whileBlocking( mCrsSelector )->setCrs( mSelectedCrs );
1406 mExtentGroupBox->setOutputCrs( mSelectedCrs );
1407 }
1408 else if ( !force4326 && mCrsDefinedByFormat )
1409 {
1410 mCrsDefinedByFormat = false;
1411 mCrsSelector->setEnabled( true );
1412 mCrsSelector->setCrs( mUserDefinedCrs );
1413 }
1414}
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:6012
@ PerFeature
Keeps the number of features and export symbology per feature.
Definition qgis.h:6014
@ PerSymbolLayer
Exports one feature per symbol layer (considering symbol levels).
Definition qgis.h:6015
@ NoSymbology
Export only data.
Definition qgis.h:6013
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:6982
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,...