QGIS API Documentation 3.43.0-Master (7996e1b587e)
qgsvectorfilewriter.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsvectorfilewriter.cpp
3 generic vector file writer
4 -------------------
5 begin : Sat Jun 16 2004
6 copyright : (C) 2004 by Tim Sutton
7 email : tim at linfiniti.com
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 ***************************************************************************/
18
19#include "qgsapplication.h"
20#include "qgsfields.h"
21
22#include "qgsgdalutils.h"
23#include "qgsfielddomain.h"
24#include "qgslogger.h"
25#include "qgsmaplayerutils.h"
26#include "qgsmessagelog.h"
28#include "qgsvectorfilewriter.h"
29#include "qgssettings.h"
30#include "qgssymbol.h"
31#include "qgssymbollayer.h"
32#include "qgslocalec.h"
33#include "qgsogrutils.h"
34#include "qgsvectorlayer.h"
35#include "qgsproviderregistry.h"
37#include "qgsreadwritelocker.h"
38
39#include <QFile>
40#include <QFileInfo>
41#include <QDir>
42#include <QTextCodec>
43#include <QTextStream>
44#include <QSet>
45#include <QMetaType>
46#include <QMutex>
47#include <QRegularExpression>
48#include <QJsonDocument>
49
50#include <cassert>
51#include <cstdlib> // size_t
52#include <limits> // std::numeric_limits
53
54#include <ogr_srs_api.h>
55#include <cpl_error.h>
56#include <cpl_conv.h>
57#include <cpl_string.h>
58#include <gdal.h>
59
64
65QVariant QgsVectorFileWriter::FieldValueConverter::convert( int /*fieldIdxInLayer*/, const QVariant &value )
66{
67 return value;
68}
69
74
75QgsVectorFileWriter::QgsVectorFileWriter( const QString &vectorFileName,
76 const QString &fileEncoding,
77 const QgsFields &fields,
78 Qgis::WkbType geometryType,
80 const QString &driverName,
81 const QStringList &datasourceOptions,
82 const QStringList &layerOptions,
83 QString *newFilename,
86 QString *newLayer,
87 const QgsCoordinateTransformContext &transformContext,
88 FieldNameSource fieldNameSource )
89 : mError( NoError )
90 , mWkbType( geometryType )
92 , mSymbologyScale( 1.0 )
93{
94 init( vectorFileName, fileEncoding, fields, geometryType,
95 srs, driverName, datasourceOptions, layerOptions, newFilename, nullptr,
96 QString(), CreateOrOverwriteFile, newLayer, sinkFlags, transformContext, fieldNameSource, nullptr );
97}
98
99QgsVectorFileWriter::QgsVectorFileWriter( const QString &vectorFileName,
100 const QString &fileEncoding,
101 const QgsFields &fields,
102 Qgis::WkbType geometryType,
104 const QString &driverName,
105 const QStringList &datasourceOptions,
106 const QStringList &layerOptions,
107 QString *newFilename,
108 Qgis::FeatureSymbologyExport symbologyExport,
109 FieldValueConverter *fieldValueConverter,
110 const QString &layerName,
112 QString *newLayer,
113 const QgsCoordinateTransformContext &transformContext,
115 FieldNameSource fieldNameSource,
116 bool includeConstraints,
117 bool setFieldDomains,
118 const QgsAbstractDatabaseProviderConnection *sourceDatabaseProviderConnection )
119 : mError( NoError )
120 , mWkbType( geometryType )
121 , mSymbologyExport( symbologyExport )
122 , mSymbologyScale( 1.0 )
123 , mIncludeConstraints( includeConstraints )
124 , mSetFieldDomains( setFieldDomains )
125{
126 init( vectorFileName, fileEncoding, fields, geometryType, srs, driverName,
127 datasourceOptions, layerOptions, newFilename, fieldValueConverter,
128 layerName, action, newLayer, sinkFlags, transformContext, fieldNameSource, sourceDatabaseProviderConnection );
129}
130
132 const QString &fileName,
133 const QgsFields &fields,
134 Qgis::WkbType geometryType,
136 const QgsCoordinateTransformContext &transformContext,
139 QString *newFilename,
140 QString *newLayer
141)
142{
144 return new QgsVectorFileWriter( fileName, options.fileEncoding, fields, geometryType, srs,
145 options.driverName, options.datasourceOptions, options.layerOptions,
146 newFilename, options.symbologyExport, options.fieldValueConverter, options.layerName,
147 options.actionOnExistingFile, newLayer, transformContext, sinkFlags, options.fieldNameSource, options.includeConstraints, options.setFieldDomains, options.sourceDatabaseProviderConnection );
149}
150
151bool QgsVectorFileWriter::supportsFeatureStyles( const QString &driverName )
152{
153 if ( driverName == QLatin1String( "MapInfo MIF" ) )
154 {
155 return true;
156 }
157 GDALDriverH gdalDriver = GDALGetDriverByName( driverName.toLocal8Bit().constData() );
158 if ( !gdalDriver )
159 return false;
160
161 char **driverMetadata = GDALGetMetadata( gdalDriver, nullptr );
162 if ( !driverMetadata )
163 return false;
164
165#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,7,0)
166 return CSLFetchBoolean( driverMetadata, GDAL_DCAP_FEATURE_STYLES_WRITE, false );
167#else
168 return CSLFetchBoolean( driverMetadata, GDAL_DCAP_FEATURE_STYLES, false );
169#endif
170}
171
172void QgsVectorFileWriter::init( QString vectorFileName,
173 QString fileEncoding,
174 const QgsFields &fields,
175 Qgis::WkbType geometryType,
177 const QString &driverName,
178 QStringList datasourceOptions,
179 QStringList layerOptions,
180 QString *newFilename,
181 FieldValueConverter *fieldValueConverter,
182 const QString &layerNameIn,
183 ActionOnExistingFile action,
184 QString *newLayer, SinkFlags sinkFlags,
185 const QgsCoordinateTransformContext &transformContext, FieldNameSource fieldNameSource,
186 const QgsAbstractDatabaseProviderConnection *sourceDatabaseProviderConnection )
187{
188#if GDAL_VERSION_NUM < GDAL_COMPUTE_VERSION(3,5,0)
189 ( void )sourceDatabaseProviderConnection;
190#endif
191
192 mRenderContext.setRendererScale( mSymbologyScale );
193
194 if ( vectorFileName.isEmpty() )
195 {
196 mErrorMessage = QObject::tr( "Empty filename given" );
198 return;
199 }
200
201 if ( driverName == QLatin1String( "MapInfo MIF" ) )
202 {
203 mOgrDriverName = QStringLiteral( "MapInfo File" );
204 }
205 else if ( driverName == QLatin1String( "SpatiaLite" ) )
206 {
207 mOgrDriverName = QStringLiteral( "SQLite" );
208 if ( !datasourceOptions.contains( QStringLiteral( "SPATIALITE=YES" ) ) )
209 {
210 datasourceOptions.append( QStringLiteral( "SPATIALITE=YES" ) );
211 }
212 }
213 else if ( driverName == QLatin1String( "DBF file" ) )
214 {
215 mOgrDriverName = QStringLiteral( "ESRI Shapefile" );
216 if ( !layerOptions.contains( QStringLiteral( "SHPT=NULL" ) ) )
217 {
218 layerOptions.append( QStringLiteral( "SHPT=NULL" ) );
219 }
221 }
222 else
223 {
224 mOgrDriverName = driverName;
225 }
226
227#if GDAL_VERSION_NUM < GDAL_COMPUTE_VERSION(3,3,1)
228 QString fidFieldName;
229 if ( mOgrDriverName == QLatin1String( "GPKG" ) )
230 {
231 for ( const QString &layerOption : layerOptions )
232 {
233 if ( layerOption.startsWith( QLatin1String( "FID=" ) ) )
234 {
235 fidFieldName = layerOption.mid( 4 );
236 break;
237 }
238 }
239 if ( fidFieldName.isEmpty() )
240 fidFieldName = QStringLiteral( "fid" );
241 }
242#endif
243
244 // find driver in OGR
245 OGRSFDriverH poDriver;
247
248 poDriver = OGRGetDriverByName( mOgrDriverName.toLocal8Bit().constData() );
249
250 if ( !poDriver )
251 {
252 mErrorMessage = QObject::tr( "OGR driver for '%1' not found (OGR error: %2)" )
253 .arg( driverName,
254 QString::fromUtf8( CPLGetLastErrorMsg() ) );
256 return;
257 }
258
259 mOgrDriverLongName = QString( GDALGetMetadataItem( poDriver, GDAL_DMD_LONGNAME, nullptr ) );
260
261 MetaData metadata;
262 bool metadataFound = driverMetadata( driverName, metadata );
263
264 if ( mOgrDriverName == QLatin1String( "ESRI Shapefile" ) )
265 {
266 if ( layerOptions.join( QString() ).toUpper().indexOf( QLatin1String( "ENCODING=" ) ) == -1 )
267 {
268 layerOptions.append( "ENCODING=" + convertCodecNameForEncodingOption( fileEncoding ) );
269 }
270
271 if ( driverName == QLatin1String( "ESRI Shapefile" ) && !vectorFileName.endsWith( QLatin1String( ".shp" ), Qt::CaseInsensitive ) )
272 {
273 vectorFileName += QLatin1String( ".shp" );
274 }
275 else if ( driverName == QLatin1String( "DBF file" ) && !vectorFileName.endsWith( QLatin1String( ".dbf" ), Qt::CaseInsensitive ) )
276 {
277 vectorFileName += QLatin1String( ".dbf" );
278 }
279
280 if ( action == CreateOrOverwriteFile || action == CreateOrOverwriteLayer )
281 deleteShapeFile( vectorFileName );
282 }
283 else
284 {
285 if ( metadataFound )
286 {
287 QStringList allExts = metadata.ext.split( ' ', Qt::SkipEmptyParts );
288 bool found = false;
289 const auto constAllExts = allExts;
290 for ( const QString &ext : constAllExts )
291 {
292 if ( vectorFileName.endsWith( '.' + ext, Qt::CaseInsensitive ) )
293 {
294 found = true;
295 break;
296 }
297 }
298
299 if ( !found )
300 {
301 vectorFileName += '.' + allExts[0];
302 }
303 }
304
305 if ( action == CreateOrOverwriteFile )
306 {
307 if ( vectorFileName.endsWith( QLatin1String( ".gdb" ), Qt::CaseInsensitive ) )
308 {
309 QDir dir( vectorFileName );
310 if ( dir.exists() )
311 {
312 QFileInfoList fileList = dir.entryInfoList(
313 QDir::NoDotAndDotDot | QDir::System | QDir::Hidden | QDir::AllDirs | QDir::Files, QDir::DirsFirst );
314 const auto constFileList = fileList;
315 for ( const QFileInfo &info : constFileList )
316 {
317 QFile::remove( info.absoluteFilePath() );
318 }
319 }
320 QDir().rmdir( vectorFileName );
321 }
322 else
323 {
324 QFile::remove( vectorFileName );
325 }
326 }
327 }
328
329 if ( metadataFound && !metadata.compulsoryEncoding.isEmpty() )
330 {
331 if ( fileEncoding.compare( metadata.compulsoryEncoding, Qt::CaseInsensitive ) != 0 )
332 {
333 QgsDebugMsgLevel( QStringLiteral( "forced %1 encoding for %2" ).arg( metadata.compulsoryEncoding, driverName ), 2 );
334 fileEncoding = metadata.compulsoryEncoding;
335 }
336
337 }
338
339 char **options = nullptr;
340 if ( !datasourceOptions.isEmpty() )
341 {
342 options = new char *[ datasourceOptions.size() + 1 ];
343 for ( int i = 0; i < datasourceOptions.size(); i++ )
344 {
345 QgsDebugMsgLevel( QStringLiteral( "-dsco=%1" ).arg( datasourceOptions[i] ), 2 );
346 options[i] = CPLStrdup( datasourceOptions[i].toUtf8().constData() );
347 }
348 options[ datasourceOptions.size()] = nullptr;
349 }
350
351 mAttrIdxToOgrIdx.remove( 0 );
352
353 // create the data source
354 if ( action == CreateOrOverwriteFile )
355 mDS.reset( OGR_Dr_CreateDataSource( poDriver, vectorFileName.toUtf8().constData(), options ) );
356 else
357 mDS.reset( OGROpen( vectorFileName.toUtf8().constData(), TRUE, nullptr ) );
358
359 if ( options )
360 {
361 for ( int i = 0; i < datasourceOptions.size(); i++ )
362 CPLFree( options[i] );
363 delete [] options;
364 options = nullptr;
365 }
366
367 if ( !mDS )
368 {
370 if ( action == CreateOrOverwriteFile )
371 mErrorMessage = QObject::tr( "Creation of data source failed (OGR error: %1)" )
372 .arg( QString::fromUtf8( CPLGetLastErrorMsg() ) );
373 else
374 mErrorMessage = QObject::tr( "Opening of data source in update mode failed (OGR error: %1)" )
375 .arg( QString::fromUtf8( CPLGetLastErrorMsg() ) );
376 return;
377 }
378
379 QString layerName( layerNameIn );
380 if ( layerName.isEmpty() )
381 layerName = QFileInfo( vectorFileName ).baseName();
382
383 if ( action == CreateOrOverwriteLayer )
384 {
385 const int layer_count = OGR_DS_GetLayerCount( mDS.get() );
386 for ( int i = 0; i < layer_count; i++ )
387 {
388 OGRLayerH hLayer = OGR_DS_GetLayer( mDS.get(), i );
389 if ( EQUAL( OGR_L_GetName( hLayer ), layerName.toUtf8().constData() ) )
390 {
391 if ( OGR_DS_DeleteLayer( mDS.get(), i ) != OGRERR_NONE )
392 {
394 mErrorMessage = QObject::tr( "Overwriting of existing layer failed (OGR error: %1)" )
395 .arg( QString::fromUtf8( CPLGetLastErrorMsg() ) );
396 return;
397 }
398 break;
399 }
400 }
401 }
402
403 if ( action == CreateOrOverwriteFile )
404 {
405 QgsDebugMsgLevel( QStringLiteral( "Created data source" ), 2 );
406 }
407 else
408 {
409 QgsDebugMsgLevel( QStringLiteral( "Opened data source in update mode" ), 2 );
410 }
411
412 // use appropriate codec
413 mCodec = QTextCodec::codecForName( fileEncoding.toLocal8Bit().constData() );
414 if ( !mCodec )
415 {
416 QgsDebugError( "error finding QTextCodec for " + fileEncoding );
417
418 QgsSettings settings;
419 QString enc = settings.value( QStringLiteral( "UI/encoding" ), "System" ).toString();
420 mCodec = QTextCodec::codecForName( enc.toLocal8Bit().constData() );
421 if ( !mCodec )
422 {
423 QgsDebugError( "error finding QTextCodec for " + enc );
424 mCodec = QTextCodec::codecForLocale();
425 Q_ASSERT( mCodec );
426 }
427 }
428
429 // consider spatial reference system of the layer
430 if ( driverName == QLatin1String( "KML" ) || driverName == QLatin1String( "LIBKML" ) || driverName == QLatin1String( "GPX" ) )
431 {
432 if ( srs.authid() != QLatin1String( "EPSG:4326" ) )
433 {
434 // Those drivers outputs WGS84 geometries, let's align our output CRS to have QGIS take charge of geometry transformation
436 mCoordinateTransform.reset( new QgsCoordinateTransform( srs, wgs84, transformContext ) );
437 srs = wgs84;
438 }
439 }
440
442
443 // datasource created, now create the output layer
444 OGRwkbGeometryType wkbType = ogrTypeFromWkbType( geometryType );
445
446 // Remove FEATURE_DATASET layer option (used for ESRI File GDB driver) if its value is not set
447 int optIndex = layerOptions.indexOf( QLatin1String( "FEATURE_DATASET=" ) );
448 if ( optIndex != -1 )
449 {
450 layerOptions.removeAt( optIndex );
451 }
452
453 if ( !layerOptions.isEmpty() )
454 {
455 options = new char *[ layerOptions.size() + 1 ];
456 for ( int i = 0; i < layerOptions.size(); i++ )
457 {
458 QgsDebugMsgLevel( QStringLiteral( "-lco=%1" ).arg( layerOptions[i] ), 2 );
459 options[i] = CPLStrdup( layerOptions[i].toUtf8().constData() );
460 }
461 options[ layerOptions.size()] = nullptr;
462 }
463
464 // disable encoding conversion of OGR Shapefile layer
465 CPLSetConfigOption( "SHAPE_ENCODING", "" );
466
467 if ( action == CreateOrOverwriteFile || action == CreateOrOverwriteLayer )
468 {
469 mLayer = OGR_DS_CreateLayer( mDS.get(), layerName.toUtf8().constData(), mOgrRef, wkbType, options );
470 if ( newLayer && mLayer )
471 {
472 *newLayer = OGR_L_GetName( mLayer );
473 if ( driverName == QLatin1String( "GPX" ) )
474 {
475 // See logic in GDAL ogr/ogrsf_frmts/gpx/ogrgpxdatasource.cpp ICreateLayer()
476 switch ( QgsWkbTypes::flatType( geometryType ) )
477 {
479 {
480 if ( !EQUAL( layerName.toUtf8().constData(), "track_points" ) &&
481 !EQUAL( layerName.toUtf8().constData(), "route_points" ) )
482 {
483 *newLayer = QStringLiteral( "waypoints" );
484 }
485 }
486 break;
487
489 {
490 const char *pszForceGPXTrack
491 = CSLFetchNameValue( options, "FORCE_GPX_TRACK" );
492 if ( pszForceGPXTrack && CPLTestBool( pszForceGPXTrack ) )
493 *newLayer = QStringLiteral( "tracks" );
494 else
495 *newLayer = QStringLiteral( "routes" );
496
497 }
498 break;
499
501 {
502 const char *pszForceGPXRoute
503 = CSLFetchNameValue( options, "FORCE_GPX_ROUTE" );
504 if ( pszForceGPXRoute && CPLTestBool( pszForceGPXRoute ) )
505 *newLayer = QStringLiteral( "routes" );
506 else
507 *newLayer = QStringLiteral( "tracks" );
508 }
509 break;
510
511 default:
512 break;
513 }
514 }
515 }
516 }
517 else if ( driverName == QLatin1String( "DGN" ) )
518 {
519 mLayer = OGR_DS_GetLayerByName( mDS.get(), "elements" );
520 }
521 else
522 {
523 mLayer = OGR_DS_GetLayerByName( mDS.get(), layerName.toUtf8().constData() );
524 }
525
526 if ( options )
527 {
528 for ( int i = 0; i < layerOptions.size(); i++ )
529 CPLFree( options[i] );
530 delete [] options;
531 options = nullptr;
532 }
533
534 if ( srs.isValid() )
535 {
536 if ( mOgrDriverName == QLatin1String( "ESRI Shapefile" ) )
537 {
538 QString layerName = vectorFileName.left( vectorFileName.indexOf( QLatin1String( ".shp" ), Qt::CaseInsensitive ) );
539 QFile prjFile( layerName + ".qpj" );
540 if ( prjFile.exists() )
541 prjFile.remove();
542 }
543 }
544
545 if ( !mLayer )
546 {
547 if ( action == CreateOrOverwriteFile || action == CreateOrOverwriteLayer )
548 mErrorMessage = QObject::tr( "Creation of layer failed (OGR error: %1)" )
549 .arg( QString::fromUtf8( CPLGetLastErrorMsg() ) );
550 else
551 mErrorMessage = QObject::tr( "Opening of layer failed (OGR error: %1)" )
552 .arg( QString::fromUtf8( CPLGetLastErrorMsg() ) );
554 return;
555 }
556
557 OGRFeatureDefnH defn = OGR_L_GetLayerDefn( mLayer );
558
559 QgsDebugMsgLevel( QStringLiteral( "created layer" ), 2 );
560
561 // create the fields
562 QgsDebugMsgLevel( "creating " + QString::number( fields.size() ) + " fields", 2 );
563
564 mFields = fields;
566 QSet<int> existingIdxs;
567
568 mFieldValueConverter = fieldValueConverter;
569
570#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,7,0)
571 if ( const char *pszCreateFieldDefnFlags = GDALGetMetadataItem( poDriver, GDAL_DMD_CREATION_FIELD_DEFN_FLAGS, nullptr ) )
572 {
573 char **papszTokens = CSLTokenizeString2( pszCreateFieldDefnFlags, " ", 0 );
574 if ( CSLFindString( papszTokens, "AlternativeName" ) >= 0 )
575 {
577 }
578 if ( CSLFindString( papszTokens, "Comment" ) >= 0 )
579 {
581 }
582 CSLDestroy( papszTokens );
583 }
584#endif
585
586 switch ( action )
587 {
591 {
592#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,5,0)
593 QSet<QString> existingDestDomainNames;
594 if ( sourceDatabaseProviderConnection )
595 {
596 char **domainNames = GDALDatasetGetFieldDomainNames( mDS.get(), nullptr );
597 for ( const char *const *iterDomainNames = domainNames; iterDomainNames && *iterDomainNames; ++iterDomainNames )
598 {
599 existingDestDomainNames.insert( QString::fromUtf8( *iterDomainNames ) );
600 }
601 CSLDestroy( domainNames );
602 }
603#endif
604#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,6,0)
605 QSet< QString > usedAlternativeNames;
606#endif
607
608 const QString ogrFidColumnName { OGR_L_GetFIDColumn( mLayer ) };
609 const int fidNameIndex = OGR_FD_GetFieldIndex( defn, ogrFidColumnName.toUtf8() );
610 // if native fid column in created layer matches an attribute from the user-specified fields, we'll be
611 // promoting that to a real attribute.
612 const bool promoteFidColumnToAttribute = !ogrFidColumnName.isEmpty() && fidNameIndex < 0 && fields.lookupField( ogrFidColumnName ) >= 0;
613 int offsetRoomForFid = promoteFidColumnToAttribute ? 1 : 0;
614
615 for ( int fldIdx = 0; fldIdx < fields.count(); ++fldIdx )
616 {
617 QgsField attrField = fields.at( fldIdx );
618
619 if ( fieldValueConverter )
620 {
621 attrField = fieldValueConverter->fieldDefinition( fields.at( fldIdx ) );
622 }
623
624 if ( action == AppendToLayerAddFields )
625 {
626 int ogrIdx = OGR_FD_GetFieldIndex( defn, mCodec->fromUnicode( attrField.name() ) );
627 if ( ogrIdx >= 0 )
628 {
629 mAttrIdxToOgrIdx.insert( fldIdx, ogrIdx );
630 continue;
631 }
632 }
633
634 QString name;
635 switch ( fieldNameSource )
636 {
637 case Original:
638 name = attrField.name();
639 break;
640
641 case PreferAlias:
642 name = !attrField.alias().isEmpty() ? attrField.alias() : attrField.name();
643 break;
644 }
645
646 OGRFieldType ogrType = OFTString; //default to string
647 OGRFieldSubType ogrSubType = OFSTNone;
648 int ogrWidth = attrField.length();
649 int ogrPrecision = attrField.precision();
650 if ( ogrPrecision > 0 )
651 ++ogrWidth;
652
653 switch ( attrField.type() )
654 {
655 case QMetaType::Type::LongLong:
656 {
657 const char *pszDataTypes = GDALGetMetadataItem( poDriver, GDAL_DMD_CREATIONFIELDDATATYPES, nullptr );
658 if ( pszDataTypes && strstr( pszDataTypes, "Integer64" ) )
659 ogrType = OFTInteger64;
660 else
661 ogrType = OFTReal;
662 ogrWidth = ogrWidth > 0 && ogrWidth <= 20 ? ogrWidth : 20;
663 ogrPrecision = 0;
664 break;
665 }
666 case QMetaType::Type::QString:
667 ogrType = OFTString;
668 if ( ( ogrWidth <= 0 || ogrWidth > 255 ) && mOgrDriverName == QLatin1String( "ESRI Shapefile" ) )
669 ogrWidth = 255;
670 break;
671
672 case QMetaType::Type::Int:
673 ogrType = OFTInteger;
674 ogrWidth = ogrWidth > 0 && ogrWidth <= 10 ? ogrWidth : 10;
675 ogrPrecision = 0;
676 break;
677
678 case QMetaType::Type::Bool:
679 ogrType = OFTInteger;
680 ogrSubType = OFSTBoolean;
681 ogrWidth = 1;
682 ogrPrecision = 0;
683 break;
684
685 case QMetaType::Type::Double:
686#if GDAL_VERSION_NUM < GDAL_COMPUTE_VERSION(3,3,1)
687 if ( mOgrDriverName == QLatin1String( "GPKG" ) && attrField.precision() == 0 && attrField.name().compare( fidFieldName, Qt::CaseInsensitive ) == 0 )
688 {
689 // Convert field to match required FID type
690 ogrType = OFTInteger64;
691 break;
692 }
693#endif
694 ogrType = OFTReal;
695 break;
696
697 case QMetaType::Type::QDate:
698 ogrType = OFTDate;
699 break;
700
701 case QMetaType::Type::QTime:
702 if ( mOgrDriverName == QLatin1String( "ESRI Shapefile" ) )
703 {
704 ogrType = OFTString;
705 ogrWidth = 12; // %02d:%02d:%06.3f
706 }
707 else
708 {
709 ogrType = OFTTime;
710 }
711 break;
712
713 case QMetaType::Type::QDateTime:
714 if ( mOgrDriverName == QLatin1String( "ESRI Shapefile" ) )
715 {
716 ogrType = OFTString;
717 ogrWidth = 24; // "%04d/%02d/%02d %02d:%02d:%06.3f"
718 }
719 else
720 {
721 ogrType = OFTDateTime;
722 }
723 break;
724
725 case QMetaType::Type::QByteArray:
726 ogrType = OFTBinary;
727 break;
728
729 case QMetaType::Type::QStringList:
730 {
731 // handle GPKG conversion to JSON
732 if ( mOgrDriverName == QLatin1String( "GPKG" ) )
733 {
734 ogrType = OFTString;
735 ogrSubType = OFSTJSON;
736 break;
737 }
738
739 const char *pszDataTypes = GDALGetMetadataItem( poDriver, GDAL_DMD_CREATIONFIELDDATATYPES, nullptr );
740 if ( pszDataTypes && strstr( pszDataTypes, "StringList" ) )
741 {
742 ogrType = OFTStringList;
743 mSupportedListSubTypes.insert( QMetaType::Type::QString );
744 }
745 else
746 {
747 ogrType = OFTString;
748 ogrWidth = 255;
749 }
750 break;
751 }
752
753 case QMetaType::Type::QVariantMap:
754 {
755 // handle GPKG conversion to JSON
756 const char *pszDataSubTypes = GDALGetMetadataItem( poDriver, GDAL_DMD_CREATIONFIELDDATASUBTYPES, nullptr );
757 if ( pszDataSubTypes && strstr( pszDataSubTypes, "JSON" ) )
758 {
759 ogrType = OFTString;
760 ogrSubType = OFSTJSON;
761 break;
762 }
763 }
764
765 //intentional fall-through
766 [[fallthrough]];
767
768 case QMetaType::Type::QVariantList:
769 // handle GPKG conversion to JSON
770 if ( mOgrDriverName == QLatin1String( "GPKG" ) )
771 {
772 ogrType = OFTString;
773 ogrSubType = OFSTJSON;
774 break;
775 }
776
777 // fall through to default for other unsupported types
778 if ( attrField.subType() == QMetaType::Type::QString )
779 {
780 const char *pszDataTypes = GDALGetMetadataItem( poDriver, GDAL_DMD_CREATIONFIELDDATATYPES, nullptr );
781 if ( pszDataTypes && strstr( pszDataTypes, "StringList" ) )
782 {
783 ogrType = OFTStringList;
784 mSupportedListSubTypes.insert( QMetaType::Type::QString );
785 }
786 else
787 {
788 ogrType = OFTString;
789 ogrWidth = 255;
790 }
791 break;
792 }
793 else if ( attrField.subType() == QMetaType::Type::Int )
794 {
795 const char *pszDataTypes = GDALGetMetadataItem( poDriver, GDAL_DMD_CREATIONFIELDDATATYPES, nullptr );
796 if ( pszDataTypes && strstr( pszDataTypes, "IntegerList" ) )
797 {
798 ogrType = OFTIntegerList;
799 mSupportedListSubTypes.insert( QMetaType::Type::Int );
800 }
801 else
802 {
803 ogrType = OFTString;
804 ogrWidth = 255;
805 }
806 break;
807 }
808 else if ( attrField.subType() == QMetaType::Type::Double )
809 {
810 const char *pszDataTypes = GDALGetMetadataItem( poDriver, GDAL_DMD_CREATIONFIELDDATATYPES, nullptr );
811 if ( pszDataTypes && strstr( pszDataTypes, "RealList" ) )
812 {
813 ogrType = OFTRealList;
814 mSupportedListSubTypes.insert( QMetaType::Type::Double );
815 }
816 else
817 {
818 ogrType = OFTString;
819 ogrWidth = 255;
820 }
821 break;
822 }
823 else if ( attrField.subType() == QMetaType::Type::LongLong )
824 {
825 const char *pszDataTypes = GDALGetMetadataItem( poDriver, GDAL_DMD_CREATIONFIELDDATATYPES, nullptr );
826 if ( pszDataTypes && strstr( pszDataTypes, "Integer64List" ) )
827 {
828 ogrType = OFTInteger64List;
829 mSupportedListSubTypes.insert( QMetaType::Type::LongLong );
830 }
831 else
832 {
833 ogrType = OFTString;
834 ogrWidth = 255;
835 }
836 break;
837 }
838 //intentional fall-through
839 [[fallthrough]];
840
841 default:
842 //assert(0 && "invalid variant type!");
843 mErrorMessage = QObject::tr( "Unsupported type for field %1" )
844 .arg( attrField.name() );
846 return;
847 }
848
849 if ( mOgrDriverName == QLatin1String( "SQLite" ) && name.compare( QLatin1String( "ogc_fid" ), Qt::CaseInsensitive ) == 0 )
850 {
851 int i;
852 for ( i = 0; i < 10; i++ )
853 {
854 name = QStringLiteral( "ogc_fid%1" ).arg( i );
855
856 int j;
857 for ( j = 0; j < fields.size() && name.compare( fields.at( j ).name(), Qt::CaseInsensitive ) != 0; j++ )
858 ;
859
860 if ( j == fields.size() )
861 break;
862 }
863
864 if ( i == 10 )
865 {
866 mErrorMessage = QObject::tr( "No available replacement for internal fieldname ogc_fid found" ).arg( attrField.name() );
868 return;
869 }
870
871 QgsMessageLog::logMessage( QObject::tr( "Reserved attribute name ogc_fid replaced with %1" ).arg( name ), QObject::tr( "OGR" ) );
872 }
873
874 // create field definition
875 gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( mCodec->fromUnicode( name ), ogrType ) );
876 if ( ogrWidth > 0 )
877 {
878 OGR_Fld_SetWidth( fld.get(), ogrWidth );
879 }
880
881 if ( ogrPrecision >= 0 )
882 {
883 OGR_Fld_SetPrecision( fld.get(), ogrPrecision );
884 }
885
886 if ( ogrSubType != OFSTNone )
887 OGR_Fld_SetSubType( fld.get(), ogrSubType );
888
889#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,6,0)
890 if ( !attrField.alias().isEmpty() )
891 {
892 QString alternativeName = attrField.alias();
893 int counter = 1;
894 while ( usedAlternativeNames.contains( alternativeName ) )
895 {
896 // field alternative names MUST be unique (at least for Geopackage, but let's apply the constraint universally)
897 alternativeName = attrField.alias() + QStringLiteral( " (%1)" ).arg( ++counter );
898 }
899 OGR_Fld_SetAlternativeName( fld.get(), mCodec->fromUnicode( alternativeName ).constData() );
900 usedAlternativeNames.insert( alternativeName );
901 }
902#endif
903#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,7,0)
904 OGR_Fld_SetComment( fld.get(), mCodec->fromUnicode( attrField.comment() ).constData() );
905#endif
906
908 {
910 {
911 OGR_Fld_SetNullable( fld.get(), false );
912 }
914 {
915 OGR_Fld_SetUnique( fld.get(), true );
916 }
917 }
918#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,5,0)
919 if ( mSetFieldDomains && sourceDatabaseProviderConnection )
920 {
921 const QString domainName = attrField.constraints().domainName();
922 if ( !domainName.isEmpty() )
923 {
924 bool canSetFieldDomainName = false;
925 if ( existingDestDomainNames.contains( domainName ) )
926 {
927 // If the target dataset already knows this field domain,
928 // we can directly assign its name to the new field.
929 canSetFieldDomainName = true;
930 }
931 else if ( GDALDatasetTestCapability( mDS.get(), ODsCAddFieldDomain ) )
932 {
933 // Otherwise, if the output dataset can create field domains,
934 // - convert the QGIS field domain to a GDAL one
935 // - register it to the GDAL dataset
936 // - if successful, note that we know that field domain (if it
937 // is shared by other fields)
938 // - assign its name to the new field.
939 try
940 {
941 std::unique_ptr<QgsFieldDomain> domain( sourceDatabaseProviderConnection->fieldDomain( domainName ) );
942 if ( domain )
943 {
944 OGRFieldDomainH hFieldDomain = QgsOgrUtils::convertFieldDomain( domain.get() );
945 if ( hFieldDomain )
946 {
947 char *pszFailureReason = nullptr;
948 if ( GDALDatasetAddFieldDomain( mDS.get(), hFieldDomain, &pszFailureReason ) )
949 {
950 existingDestDomainNames.insert( domainName );
951 canSetFieldDomainName = true;
952 }
953 else
954 {
955 QgsDebugError( QStringLiteral( "cannot create field domain: %1" ).arg( pszFailureReason ) );
956 }
957 CPLFree( pszFailureReason );
958 OGR_FldDomain_Destroy( hFieldDomain );
959 }
960 }
961 }
963 {
964 QgsDebugError( QStringLiteral( "Cannot retrieve field domain: %1" ).arg( domainName ) );
965 }
966 }
967 if ( canSetFieldDomainName )
968 {
969 OGR_Fld_SetDomainName( fld.get(), domainName.toUtf8().toStdString().c_str() );
970 }
971 }
972 }
973#endif
974
975 // create the field
976 QgsDebugMsgLevel( "creating field " + attrField.name() +
977 " type " + QString( QVariant::typeToName( attrField.type() ) ) +
978 " width " + QString::number( ogrWidth ) +
979 " precision " + QString::number( ogrPrecision ), 2 );
980 if ( OGR_L_CreateField( mLayer, fld.get(), true ) != OGRERR_NONE )
981 {
982 QgsDebugError( "error creating field " + attrField.name() );
983 mErrorMessage = QObject::tr( "Creation of field %1 (%2) failed (OGR error: %3)" )
984 .arg( attrField.name(),
985 QVariant::typeToName( attrField.type() ),
986 QString::fromUtf8( CPLGetLastErrorMsg() ) );
988 return;
989 }
990
991 int ogrIdx = OGR_FD_GetFieldIndex( defn, mCodec->fromUnicode( name ) );
992 QgsDebugMsgLevel( QStringLiteral( "returned field index for %1: %2" ).arg( name ).arg( ogrIdx ), 2 );
993 if ( ogrIdx < 0 || existingIdxs.contains( ogrIdx ) )
994 {
995 // GDAL 1.7 not just truncates, but launders more aggressivly.
996 ogrIdx = OGR_FD_GetFieldCount( defn ) - 1;
997
998 if ( ogrIdx < 0 )
999 {
1000 QgsDebugError( "error creating field " + attrField.name() );
1001 mErrorMessage = QObject::tr( "Created field %1 not found (OGR error: %2)" )
1002 .arg( attrField.name(),
1003 QString::fromUtf8( CPLGetLastErrorMsg() ) );
1005 return;
1006 }
1007 }
1008
1009 if ( promoteFidColumnToAttribute )
1010 {
1011 if ( ogrFidColumnName.compare( attrField.name(), Qt::CaseInsensitive ) == 0 )
1012 {
1013 ogrIdx = 0;
1014 offsetRoomForFid = 0;
1015 }
1016 else
1017 {
1018 // shuffle to make space for fid column
1019 ogrIdx += offsetRoomForFid;
1020 }
1021 }
1022
1023 existingIdxs.insert( ogrIdx );
1024 mAttrIdxToOgrIdx.insert( fldIdx, ogrIdx );
1025 }
1026 }
1027 break;
1028
1030 {
1031 for ( int fldIdx = 0; fldIdx < fields.count(); ++fldIdx )
1032 {
1033 QgsField attrField = fields.at( fldIdx );
1034 QString name( attrField.name() );
1035 int ogrIdx = OGR_FD_GetFieldIndex( defn, mCodec->fromUnicode( name ) );
1036 if ( ogrIdx >= 0 )
1037 mAttrIdxToOgrIdx.insert( fldIdx, ogrIdx );
1038 }
1039 }
1040 break;
1041 }
1042
1043 // Geopackages require a unique feature id. If the input feature stream cannot guarantee
1044 // the uniqueness of the FID column, we drop it and let OGR generate new ones
1045 if ( sinkFlags.testFlag( QgsFeatureSink::RegeneratePrimaryKey ) && driverName == QLatin1String( "GPKG" ) )
1046 {
1047 int fidIdx = fields.lookupField( QStringLiteral( "FID" ) );
1048
1049 if ( fidIdx >= 0 )
1050 mAttrIdxToOgrIdx.remove( fidIdx );
1051 }
1052
1053 QgsDebugMsgLevel( QStringLiteral( "Done creating fields" ), 2 );
1054
1055 mWkbType = geometryType;
1056
1057 if ( newFilename )
1058 *newFilename = vectorFileName;
1059
1060 // enabling transaction on databases that support it
1061 mUsingTransaction = true;
1062 if ( OGRERR_NONE != OGR_L_StartTransaction( mLayer ) )
1063 {
1064 mUsingTransaction = false;
1065 }
1066}
1067
1069{
1070 return OGR_G_CreateGeometry( ogrTypeFromWkbType( wkbType ) );
1071}
1072
1074class QgsVectorFileWriterMetadataContainer
1075{
1076 public:
1077
1078 QgsVectorFileWriterMetadataContainer()
1079 {
1080 QMap<QString, QgsVectorFileWriter::Option *> datasetOptions;
1081 QMap<QString, QgsVectorFileWriter::Option *> layerOptions;
1082
1083 // Arrow
1084 datasetOptions.clear();
1085 layerOptions.clear();
1086
1087 layerOptions.insert( QStringLiteral( "COMPRESSION" ), new QgsVectorFileWriter::SetOption(
1088 QObject::tr( "Compression method." ),
1089 QStringList()
1090 << QStringLiteral( "UNCOMPRESSED" )
1091 << QStringLiteral( "ZSTD" )
1092 << QStringLiteral( "LZ4" ),
1093 QStringLiteral( "LZ4" ), // Default value
1094 false // Allow None
1095 ) );
1096
1097 layerOptions.insert( QStringLiteral( "GEOMETRY_ENCODING" ), new QgsVectorFileWriter::SetOption(
1098 QObject::tr( "Geometry encoding." ),
1099 QStringList()
1100 << QStringLiteral( "GEOARROW" )
1101 << QStringLiteral( "WKB" )
1102 << QStringLiteral( "WKT" ),
1103 QStringLiteral( "GEOARROW" ), // Default value
1104 false // Allow None
1105 ) );
1106
1107 layerOptions.insert( QStringLiteral( "BATCH_SIZE" ), new QgsVectorFileWriter::IntOption(
1108 QObject::tr( "Maximum number of rows per batch." ),
1109 65536 // Default value
1110 ) );
1111
1112 layerOptions.insert( QStringLiteral( "FID" ), new QgsVectorFileWriter::StringOption(
1113 QObject::tr( "Name for the feature identifier column" ),
1114 QString() // Default value
1115 ) );
1116
1117 layerOptions.insert( QStringLiteral( "GEOMETRY_NAME" ), new QgsVectorFileWriter::StringOption(
1118 QObject::tr( "Name for the geometry column" ),
1119 QStringLiteral( "geometry" ) // Default value
1120 ) );
1121
1122 driverMetadata.insert( QStringLiteral( "Arrow" ),
1124 QStringLiteral( "(Geo)Arrow" ),
1125 QObject::tr( "(Geo)Arrow" ),
1126 QStringLiteral( "*.arrow *.feather *.arrows *.ipc" ),
1127 QStringLiteral( "arrow" ),
1128 datasetOptions,
1129 layerOptions,
1130 QStringLiteral( "UTF-8" )
1131 )
1132 );
1133
1134 // Arc/Info ASCII Coverage
1135 datasetOptions.clear();
1136 layerOptions.clear();
1137
1138 driverMetadata.insert( QStringLiteral( "AVCE00" ),
1140 QStringLiteral( "Arc/Info ASCII Coverage" ),
1141 QObject::tr( "Arc/Info ASCII Coverage" ),
1142 QStringLiteral( "*.e00" ),
1143 QStringLiteral( "e00" ),
1144 datasetOptions,
1145 layerOptions
1146 )
1147 );
1148
1149 // Comma Separated Value
1150 datasetOptions.clear();
1151 layerOptions.clear();
1152
1153 layerOptions.insert( QStringLiteral( "LINEFORMAT" ), new QgsVectorFileWriter::SetOption(
1154 QObject::tr( "By default when creating new .csv files they "
1155 "are created with the line termination conventions "
1156 "of the local platform (CR/LF on Win32 or LF on all other systems). "
1157 "This may be overridden through the use of the LINEFORMAT option." ),
1158 QStringList()
1159 << QStringLiteral( "CRLF" )
1160 << QStringLiteral( "LF" ),
1161 QString(), // Default value
1162 true // Allow None
1163 ) );
1164
1165 layerOptions.insert( QStringLiteral( "GEOMETRY" ), new QgsVectorFileWriter::SetOption(
1166 QObject::tr( "By default, the geometry of a feature written to a .csv file is discarded. "
1167 "It is possible to export the geometry in its WKT representation by "
1168 "specifying GEOMETRY=AS_WKT. It is also possible to export point geometries "
1169 "into their X,Y,Z components by specifying GEOMETRY=AS_XYZ, GEOMETRY=AS_XY "
1170 "or GEOMETRY=AS_YX." ),
1171 QStringList()
1172 << QStringLiteral( "AS_WKT" )
1173 << QStringLiteral( "AS_XYZ" )
1174 << QStringLiteral( "AS_XY" )
1175 << QStringLiteral( "AS_YX" ),
1176 QString(), // Default value
1177 true // Allow None
1178 ) );
1179
1180 layerOptions.insert( QStringLiteral( "CREATE_CSVT" ), new QgsVectorFileWriter::BoolOption(
1181 QObject::tr( "Create the associated .csvt file to describe the type of each "
1182 "column of the layer and its optional width and precision." ),
1183 false // Default value
1184 ) );
1185
1186 layerOptions.insert( QStringLiteral( "SEPARATOR" ), new QgsVectorFileWriter::SetOption(
1187 QObject::tr( "Field separator character." ),
1188 QStringList()
1189 << QStringLiteral( "COMMA" )
1190 << QStringLiteral( "SEMICOLON" )
1191 << QStringLiteral( "TAB" ),
1192 QStringLiteral( "COMMA" ) // Default value
1193 ) );
1194
1195 layerOptions.insert( QStringLiteral( "STRING_QUOTING" ), new QgsVectorFileWriter::SetOption(
1196 QObject::tr( "Double-quote strings. IF_AMBIGUOUS means that string values that look like numbers will be quoted." ),
1197 QStringList()
1198 << QStringLiteral( "IF_NEEDED" )
1199 << QStringLiteral( "IF_AMBIGUOUS" )
1200 << QStringLiteral( "ALWAYS" ),
1201 QStringLiteral( "IF_AMBIGUOUS" ) // Default value
1202 ) );
1203
1204 layerOptions.insert( QStringLiteral( "WRITE_BOM" ), new QgsVectorFileWriter::BoolOption(
1205 QObject::tr( "Write a UTF-8 Byte Order Mark (BOM) at the start of the file." ),
1206 false // Default value
1207 ) );
1208
1209 driverMetadata.insert( QStringLiteral( "CSV" ),
1211 QStringLiteral( "Comma Separated Value [CSV]" ),
1212 QObject::tr( "Comma Separated Value [CSV]" ),
1213 QStringLiteral( "*.csv" ),
1214 QStringLiteral( "csv" ),
1215 datasetOptions,
1216 layerOptions
1217 )
1218 );
1219
1220 // FlatGeobuf
1221 datasetOptions.clear();
1222 layerOptions.clear();
1223
1224 driverMetadata.insert( QStringLiteral( "FlatGeobuf" ),
1226 QStringLiteral( "FlatGeobuf" ),
1227 QObject::tr( "FlatGeobuf" ),
1228 QStringLiteral( "*.fgb" ),
1229 QStringLiteral( "fgb" ),
1230 datasetOptions,
1231 layerOptions,
1232 QStringLiteral( "UTF-8" )
1233 )
1234 );
1235
1236 // ESRI Shapefile
1237 datasetOptions.clear();
1238 layerOptions.clear();
1239
1240 layerOptions.insert( QStringLiteral( "SHPT" ), new QgsVectorFileWriter::SetOption(
1241 QObject::tr( "Override the type of shapefile created. "
1242 "Can be one of NULL for a simple .dbf file with no .shp file, POINT, "
1243 "ARC, POLYGON or MULTIPOINT for 2D, or POINTZ, ARCZ, POLYGONZ or "
1244 "MULTIPOINTZ for 3D;" ) +
1245 QObject::tr( " POINTM, ARCM, POLYGONM or MULTIPOINTM for measured geometries"
1246 " and POINTZM, ARCZM, POLYGONZM or MULTIPOINTZM for 3D measured"
1247 " geometries." ) +
1248 QObject::tr( " MULTIPATCH files are supported since GDAL 2.2." ) +
1249 ""
1250 , QStringList()
1251 << QStringLiteral( "NULL" )
1252 << QStringLiteral( "POINT" )
1253 << QStringLiteral( "ARC" )
1254 << QStringLiteral( "POLYGON" )
1255 << QStringLiteral( "MULTIPOINT" )
1256 << QStringLiteral( "POINTZ" )
1257 << QStringLiteral( "ARCZ" )
1258 << QStringLiteral( "POLYGONZ" )
1259 << QStringLiteral( "MULTIPOINTZ" )
1260 << QStringLiteral( "POINTM" )
1261 << QStringLiteral( "ARCM" )
1262 << QStringLiteral( "POLYGONM" )
1263 << QStringLiteral( "MULTIPOINTM" )
1264 << QStringLiteral( "POINTZM" )
1265 << QStringLiteral( "ARCZM" )
1266 << QStringLiteral( "POLYGONZM" )
1267 << QStringLiteral( "MULTIPOINTZM" )
1268 << QStringLiteral( "MULTIPATCH" )
1269 << QString(),
1270 QString(), // Default value
1271 true // Allow None
1272 ) );
1273
1274 // there does not seem to be a reason to provide this option to the user again
1275 // as we set encoding for shapefiles based on "fileEncoding" parameter passed to the writer
1276#if 0
1277 layerOptions.insert( "ENCODING", new QgsVectorFileWriter::SetOption(
1278 QObject::tr( "Set the encoding value in the DBF file. "
1279 "The default value is LDID/87. It is not clear "
1280 "what other values may be appropriate." ),
1281 QStringList()
1282 << "LDID/87",
1283 "LDID/87" // Default value
1284 ) );
1285#endif
1286
1287 layerOptions.insert( QStringLiteral( "RESIZE" ), new QgsVectorFileWriter::BoolOption(
1288 QObject::tr( "Set to YES to resize fields to their optimal size." ),
1289 false // Default value
1290 ) );
1291
1292 driverMetadata.insert( QStringLiteral( "ESRI" ),
1294 QStringLiteral( "ESRI Shapefile" ),
1295 QObject::tr( "ESRI Shapefile" ),
1296 QStringLiteral( "*.shp" ),
1297 QStringLiteral( "shp" ),
1298 datasetOptions,
1299 layerOptions
1300 )
1301 );
1302
1303 // DBF File
1304 datasetOptions.clear();
1305 layerOptions.clear();
1306
1307 driverMetadata.insert( QStringLiteral( "DBF File" ),
1309 QStringLiteral( "DBF File" ),
1310 QObject::tr( "DBF File" ),
1311 QStringLiteral( "*.dbf" ),
1312 QStringLiteral( "dbf" ),
1313 datasetOptions,
1314 layerOptions
1315 )
1316 );
1317
1318 // GeoJSON
1319 datasetOptions.clear();
1320 layerOptions.clear();
1321
1322 layerOptions.insert( QStringLiteral( "WRITE_BBOX" ), new QgsVectorFileWriter::BoolOption(
1323 QObject::tr( "Set to YES to write a bbox property with the bounding box "
1324 "of the geometries at the feature and feature collection level." ),
1325 false // Default value
1326 ) );
1327
1328 layerOptions.insert( QStringLiteral( "COORDINATE_PRECISION" ), new QgsVectorFileWriter::IntOption(
1329 QObject::tr( "Maximum number of figures after decimal separator to write in coordinates. "
1330 "Defaults to 15. Truncation will occur to remove trailing zeros." ),
1331 15 // Default value
1332 ) );
1333
1334 layerOptions.insert( QStringLiteral( "RFC7946" ), new QgsVectorFileWriter::BoolOption(
1335 QObject::tr( "Whether to use RFC 7946 standard. "
1336 "If disabled GeoJSON 2008 initial version will be used. "
1337 "Default is NO (thus GeoJSON 2008). See also Documentation (via Help button)" ),
1338 false // Default value
1339 ) );
1340
1341 driverMetadata.insert( QStringLiteral( "GeoJSON" ),
1343 QStringLiteral( "GeoJSON" ),
1344 QObject::tr( "GeoJSON" ),
1345 QStringLiteral( "*.geojson" ),
1346 QStringLiteral( "geojson" ),
1347 datasetOptions,
1348 layerOptions,
1349 QStringLiteral( "UTF-8" )
1350 )
1351 );
1352
1353 // GeoJSONSeq
1354 datasetOptions.clear();
1355 layerOptions.clear();
1356
1357 layerOptions.insert( QStringLiteral( "COORDINATE_PRECISION" ), new QgsVectorFileWriter::IntOption(
1358 QObject::tr( "Maximum number of figures after decimal separator to write in coordinates. "
1359 "Defaults to 15. Truncation will occur to remove trailing zeros." ),
1360 15 // Default value
1361 ) );
1362
1363 layerOptions.insert( QStringLiteral( "RS" ), new QgsVectorFileWriter::BoolOption(
1364 QObject::tr( "Whether to start records with the RS=0x1E character (RFC 8142 standard). "
1365 "Defaults to NO: Newline Delimited JSON (geojsonl). \n"
1366 "If set to YES: RFC 8142 standard: GeoJSON Text Sequences (geojsons)." ),
1367 false // Default value = NO
1368 ) );
1369
1370 driverMetadata.insert( QStringLiteral( "GeoJSONSeq" ),
1372 QStringLiteral( "GeoJSON - Newline Delimited" ),
1373 QObject::tr( "GeoJSON - Newline Delimited" ),
1374 QStringLiteral( "*.geojsonl *.geojsons *.json" ),
1375 QStringLiteral( "geojsonl geojsons json" ),
1376 datasetOptions,
1377 layerOptions,
1378 QStringLiteral( "UTF-8" )
1379 )
1380 );
1381
1382 // GeoRSS
1383 datasetOptions.clear();
1384 layerOptions.clear();
1385
1386 datasetOptions.insert( QStringLiteral( "FORMAT" ), new QgsVectorFileWriter::SetOption(
1387 QObject::tr( "whether the document must be in RSS 2.0 or Atom 1.0 format. "
1388 "Default value : RSS" ),
1389 QStringList()
1390 << QStringLiteral( "RSS" )
1391 << QStringLiteral( "ATOM" ),
1392 QStringLiteral( "RSS" ) // Default value
1393 ) );
1394
1395 datasetOptions.insert( QStringLiteral( "GEOM_DIALECT" ), new QgsVectorFileWriter::SetOption(
1396 QObject::tr( "The encoding of location information. Default value : SIMPLE. "
1397 "W3C_GEO only supports point geometries. "
1398 "SIMPLE or W3C_GEO only support geometries in geographic WGS84 coordinates." ),
1399 QStringList()
1400 << QStringLiteral( "SIMPLE" )
1401 << QStringLiteral( "GML" )
1402 << QStringLiteral( "W3C_GEO" ),
1403 QStringLiteral( "SIMPLE" ) // Default value
1404 ) );
1405
1406 datasetOptions.insert( QStringLiteral( "USE_EXTENSIONS" ), new QgsVectorFileWriter::BoolOption(
1407 QObject::tr( "If defined to YES, extension fields will be written. "
1408 "If the field name not found in the base schema matches "
1409 "the foo_bar pattern, foo will be considered as the namespace "
1410 "of the element, and a <foo:bar> element will be written. "
1411 "Otherwise, elements will be written in the <ogr:> namespace." ),
1412 false // Default value
1413 ) );
1414
1415 datasetOptions.insert( QStringLiteral( "WRITE_HEADER_AND_FOOTER" ), new QgsVectorFileWriter::BoolOption(
1416 QObject::tr( "If defined to NO, only <entry> or <item> elements will be written. "
1417 "The user will have to provide the appropriate header and footer of the document." ),
1418 true // Default value
1419 ) );
1420
1421 datasetOptions.insert( QStringLiteral( "HEADER" ), new QgsVectorFileWriter::StringOption(
1422 QObject::tr( "XML content that will be put between the <channel> element and the "
1423 "first <item> element for a RSS document, or between the xml tag and "
1424 "the first <entry> element for an Atom document." ),
1425 QString() // Default value
1426 ) );
1427
1428 datasetOptions.insert( QStringLiteral( "TITLE" ), new QgsVectorFileWriter::StringOption(
1429 QObject::tr( "Value put inside the <title> element in the header. "
1430 "If not provided, a dummy value will be used as that element is compulsory." ),
1431 QString() // Default value
1432 ) );
1433
1434 datasetOptions.insert( QStringLiteral( "DESCRIPTION" ), new QgsVectorFileWriter::StringOption(
1435 QObject::tr( "Value put inside the <description> element in the header. "
1436 "If not provided, a dummy value will be used as that element is compulsory." ),
1437 QString() // Default value
1438 ) );
1439
1440 datasetOptions.insert( QStringLiteral( "LINK" ), new QgsVectorFileWriter::StringOption(
1441 QObject::tr( "Value put inside the <link> element in the header. "
1442 "If not provided, a dummy value will be used as that element is compulsory." ),
1443 QString() // Default value
1444 ) );
1445
1446 datasetOptions.insert( QStringLiteral( "UPDATED" ), new QgsVectorFileWriter::StringOption(
1447 QObject::tr( "Value put inside the <updated> element in the header. "
1448 "Should be formatted as a XML datetime. "
1449 "If not provided, a dummy value will be used as that element is compulsory." ),
1450 QString() // Default value
1451 ) );
1452
1453 datasetOptions.insert( QStringLiteral( "AUTHOR_NAME" ), new QgsVectorFileWriter::StringOption(
1454 QObject::tr( "Value put inside the <author><name> element in the header. "
1455 "If not provided, a dummy value will be used as that element is compulsory." ),
1456 QString() // Default value
1457 ) );
1458
1459 datasetOptions.insert( QStringLiteral( "ID" ), new QgsVectorFileWriter::StringOption(
1460 QObject::tr( "Value put inside the <id> element in the header. "
1461 "If not provided, a dummy value will be used as that element is compulsory." ),
1462 QString() // Default value
1463 ) );
1464
1465 driverMetadata.insert( QStringLiteral( "GeoRSS" ),
1467 QStringLiteral( "GeoRSS" ),
1468 QObject::tr( "GeoRSS" ),
1469 QStringLiteral( "*.xml" ),
1470 QStringLiteral( "xml" ),
1471 datasetOptions,
1472 layerOptions,
1473 QStringLiteral( "UTF-8" )
1474 )
1475 );
1476
1477 // Geography Markup Language [GML]
1478 datasetOptions.clear();
1479 layerOptions.clear();
1480
1481 datasetOptions.insert( QStringLiteral( "XSISCHEMAURI" ), new QgsVectorFileWriter::StringOption(
1482 QObject::tr( "If provided, this URI will be inserted as the schema location. "
1483 "Note that the schema file isn't actually accessed by OGR, so it "
1484 "is up to the user to ensure it will match the schema of the OGR "
1485 "produced GML data file." ),
1486 QString() // Default value
1487 ) );
1488
1489 datasetOptions.insert( QStringLiteral( "XSISCHEMA" ), new QgsVectorFileWriter::SetOption(
1490 QObject::tr( "This writes a GML application schema file to a corresponding "
1491 ".xsd file (with the same basename). If INTERNAL is used the "
1492 "schema is written within the GML file, but this is experimental "
1493 "and almost certainly not valid XML. "
1494 "OFF disables schema generation (and is implicit if XSISCHEMAURI is used)." ),
1495 QStringList()
1496 << QStringLiteral( "EXTERNAL" )
1497 << QStringLiteral( "INTERNAL" )
1498 << QStringLiteral( "OFF" ),
1499 QStringLiteral( "EXTERNAL" ) // Default value
1500 ) );
1501
1502 datasetOptions.insert( QStringLiteral( "PREFIX" ), new QgsVectorFileWriter::StringOption(
1503 QObject::tr( "This is the prefix for the application target namespace." ),
1504 QStringLiteral( "ogr" ) // Default value
1505 ) );
1506
1507 datasetOptions.insert( QStringLiteral( "STRIP_PREFIX" ), new QgsVectorFileWriter::BoolOption(
1508 QObject::tr( "Can be set to TRUE to avoid writing the prefix of the "
1509 "application target namespace in the GML file." ),
1510 false // Default value
1511 ) );
1512
1513 datasetOptions.insert( QStringLiteral( "TARGET_NAMESPACE" ), new QgsVectorFileWriter::StringOption(
1514 QObject::tr( "Defaults to 'http://ogr.maptools.org/'. "
1515 "This is the application target namespace." ),
1516 QStringLiteral( "http://ogr.maptools.org/" ) // Default value
1517 ) );
1518
1519 datasetOptions.insert( QStringLiteral( "FORMAT" ), new QgsVectorFileWriter::SetOption(
1520 QObject::tr( "GML version to use." ),
1521 QStringList()
1522 << QStringLiteral( "GML2" )
1523 << QStringLiteral( "GML3" )
1524 << QStringLiteral( "GML3Deegree" )
1525 << QStringLiteral( "GML3.2" ),
1526 QStringLiteral( "GML3.2" ) // Default value
1527 ) );
1528
1529 datasetOptions.insert( QStringLiteral( "GML3_LONGSRS" ), new QgsVectorFileWriter::BoolOption(
1530 QObject::tr( "Only valid when FORMAT=GML3/GML3Degree/GML3.2. Default to YES. " //needs review here
1531 "If YES, SRS with EPSG authority will be written with the "
1532 "'urn:ogc:def:crs:EPSG::' prefix. In the case the SRS is a "
1533 "geographic SRS without explicit AXIS order, but that the same "
1534 "SRS authority code imported with ImportFromEPSGA() should be "
1535 "treated as lat/long, then the function will take care of coordinate "
1536 "order swapping. If set to NO, SRS with EPSG authority will be "
1537 "written with the 'EPSG:' prefix, even if they are in lat/long order." ),
1538 true // Default value
1539 ) );
1540
1541 datasetOptions.insert( QStringLiteral( "WRITE_FEATURE_BOUNDED_BY" ), new QgsVectorFileWriter::BoolOption(
1542 QObject::tr( "only valid when FORMAT=GML3/GML3Degree/GML3.2) Default to YES. "
1543 "If set to NO, the <gml:boundedBy> element will not be written for "
1544 "each feature." ),
1545 true // Default value
1546 ) );
1547
1548 datasetOptions.insert( QStringLiteral( "SPACE_INDENTATION" ), new QgsVectorFileWriter::BoolOption(
1549 QObject::tr( "Default to YES. If YES, the output will be indented with spaces "
1550 "for more readability, but at the expense of file size." ),
1551 true // Default value
1552 ) );
1553
1554
1555 driverMetadata.insert( QStringLiteral( "GML" ),
1557 QStringLiteral( "Geography Markup Language [GML]" ),
1558 QObject::tr( "Geography Markup Language [GML]" ),
1559 QStringLiteral( "*.gml" ),
1560 QStringLiteral( "gml" ),
1561 datasetOptions,
1562 layerOptions,
1563 QStringLiteral( "UTF-8" )
1564 )
1565 );
1566
1567 // GeoPackage
1568 datasetOptions.clear();
1569 layerOptions.clear();
1570
1571 layerOptions.insert( QStringLiteral( "IDENTIFIER" ), new QgsVectorFileWriter::StringOption(
1572 QObject::tr( "Human-readable identifier (e.g. short name) for the layer content" ),
1573 QString() // Default value
1574 ) );
1575
1576 layerOptions.insert( QStringLiteral( "DESCRIPTION" ), new QgsVectorFileWriter::StringOption(
1577 QObject::tr( "Human-readable description for the layer content" ),
1578 QString() // Default value
1579 ) );
1580
1581 layerOptions.insert( QStringLiteral( "FID" ), new QgsVectorFileWriter::StringOption(
1582 QObject::tr( "Name for the feature identifier column" ),
1583 QStringLiteral( "fid" ) // Default value
1584 ) );
1585
1586 layerOptions.insert( QStringLiteral( "GEOMETRY_NAME" ), new QgsVectorFileWriter::StringOption(
1587 QObject::tr( "Name for the geometry column" ),
1588 QStringLiteral( "geom" ) // Default value
1589 ) );
1590
1591 layerOptions.insert( QStringLiteral( "SPATIAL_INDEX" ), new QgsVectorFileWriter::BoolOption(
1592 QObject::tr( "If a spatial index must be created." ),
1593 true // Default value
1594 ) );
1595
1596 driverMetadata.insert( QStringLiteral( "GPKG" ),
1598 QStringLiteral( "GeoPackage" ),
1599 QObject::tr( "GeoPackage" ),
1600#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,7,0)
1601 QStringLiteral( "*.gpkg *.gpkg.zip" ),
1602#else
1603 QStringLiteral( "*.gpkg" ),
1604#endif
1605 QStringLiteral( "gpkg" ),
1606 datasetOptions,
1607 layerOptions,
1608 QStringLiteral( "UTF-8" )
1609 )
1610 );
1611
1612 // Generic Mapping Tools [GMT]
1613 datasetOptions.clear();
1614 layerOptions.clear();
1615
1616 driverMetadata.insert( QStringLiteral( "GMT" ),
1618 QStringLiteral( "Generic Mapping Tools [GMT]" ),
1619 QObject::tr( "Generic Mapping Tools [GMT]" ),
1620 QStringLiteral( "*.gmt" ),
1621 QStringLiteral( "gmt" ),
1622 datasetOptions,
1623 layerOptions
1624 )
1625 );
1626
1627 // GPS eXchange Format [GPX]
1628 datasetOptions.clear();
1629 layerOptions.clear();
1630
1631 layerOptions.insert( QStringLiteral( "FORCE_GPX_TRACK" ), new QgsVectorFileWriter::BoolOption(
1632 QObject::tr( "By default when writing a layer whose features are of "
1633 "type wkbLineString, the GPX driver chooses to write "
1634 "them as routes. If FORCE_GPX_TRACK=YES is specified, "
1635 "they will be written as tracks." ),
1636 false // Default value
1637 ) );
1638
1639 layerOptions.insert( QStringLiteral( "FORCE_GPX_ROUTE" ), new QgsVectorFileWriter::BoolOption(
1640 QObject::tr( "By default when writing a layer whose features are of "
1641 "type wkbMultiLineString, the GPX driver chooses to write "
1642 "them as tracks. If FORCE_GPX_ROUTE=YES is specified, "
1643 "they will be written as routes, provided that the multilines "
1644 "are composed of only one single line." ),
1645 false // Default value
1646 ) );
1647
1648 datasetOptions.insert( QStringLiteral( "GPX_USE_EXTENSIONS" ), new QgsVectorFileWriter::BoolOption(
1649 QObject::tr( "If GPX_USE_EXTENSIONS=YES is specified, "
1650 "extra fields will be written inside the <extensions> tag." ),
1651 false // Default value
1652 ) );
1653
1654 datasetOptions.insert( QStringLiteral( "GPX_EXTENSIONS_NS" ), new QgsVectorFileWriter::StringOption(
1655 QObject::tr( "Only used if GPX_USE_EXTENSIONS=YES and GPX_EXTENSIONS_NS_URL "
1656 "is set. The namespace value used for extension tags. By default, 'ogr'." ),
1657 QStringLiteral( "ogr" ) // Default value
1658 ) );
1659
1660 datasetOptions.insert( QStringLiteral( "GPX_EXTENSIONS_NS_URL" ), new QgsVectorFileWriter::StringOption(
1661 QObject::tr( "Only used if GPX_USE_EXTENSIONS=YES and GPX_EXTENSIONS_NS "
1662 "is set. The namespace URI. By default, 'http://osgeo.org/gdal'." ),
1663 QStringLiteral( "http://osgeo.org/gdal" ) // Default value
1664 ) );
1665
1666 datasetOptions.insert( QStringLiteral( "LINEFORMAT" ), new QgsVectorFileWriter::SetOption(
1667 QObject::tr( "By default files are created with the line termination "
1668 "conventions of the local platform (CR/LF on win32 or LF "
1669 "on all other systems). This may be overridden through use "
1670 "of the LINEFORMAT layer creation option which may have a value "
1671 "of CRLF (DOS format) or LF (Unix format)." ),
1672 QStringList()
1673 << QStringLiteral( "CRLF" )
1674 << QStringLiteral( "LF" ),
1675 QString(), // Default value
1676 true // Allow None
1677 ) );
1678
1679 driverMetadata.insert( QStringLiteral( "GPX" ),
1681 QStringLiteral( "GPS eXchange Format [GPX]" ),
1682 QObject::tr( "GPS eXchange Format [GPX]" ),
1683 QStringLiteral( "*.gpx" ),
1684 QStringLiteral( "gpx" ),
1685 datasetOptions,
1686 layerOptions,
1687 QStringLiteral( "UTF-8" )
1688 )
1689 );
1690
1691 // INTERLIS 1
1692 datasetOptions.clear();
1693 layerOptions.clear();
1694
1695 driverMetadata.insert( QStringLiteral( "Interlis 1" ),
1697 QStringLiteral( "INTERLIS 1" ),
1698 QObject::tr( "INTERLIS 1" ),
1699 QStringLiteral( "*.itf *.xml *.ili" ),
1700 QStringLiteral( "ili" ),
1701 datasetOptions,
1702 layerOptions
1703 )
1704 );
1705
1706 // INTERLIS 2
1707 datasetOptions.clear();
1708 layerOptions.clear();
1709
1710 driverMetadata.insert( QStringLiteral( "Interlis 2" ),
1712 QStringLiteral( "INTERLIS 2" ),
1713 QObject::tr( "INTERLIS 2" ),
1714 QStringLiteral( "*.xtf *.xml *.ili" ),
1715 QStringLiteral( "ili" ),
1716 datasetOptions,
1717 layerOptions
1718 )
1719 );
1720
1721 // Keyhole Markup Language [KML]
1722 datasetOptions.clear();
1723 layerOptions.clear();
1724
1725 datasetOptions.insert( QStringLiteral( "NameField" ), new QgsVectorFileWriter::StringOption(
1726 QObject::tr( "Allows you to specify the field to use for the KML <name> element." ),
1727 QStringLiteral( "Name" ) // Default value
1728 ) );
1729
1730 datasetOptions.insert( QStringLiteral( "DescriptionField" ), new QgsVectorFileWriter::StringOption(
1731 QObject::tr( "Allows you to specify the field to use for the KML <description> element." ),
1732 QStringLiteral( "Description" ) // Default value
1733 ) );
1734
1735 datasetOptions.insert( QStringLiteral( "AltitudeMode" ), new QgsVectorFileWriter::SetOption(
1736 QObject::tr( "Allows you to specify the AltitudeMode to use for KML geometries. "
1737 "This will only affect 3D geometries and must be one of the valid KML options." ),
1738 QStringList()
1739 << QStringLiteral( "clampToGround" )
1740 << QStringLiteral( "relativeToGround" )
1741 << QStringLiteral( "absolute" ),
1742 QStringLiteral( "relativeToGround" ) // Default value
1743 ) );
1744
1745 datasetOptions.insert( QStringLiteral( "DOCUMENT_ID" ), new QgsVectorFileWriter::StringOption(
1746 QObject::tr( "The DOCUMENT_ID datasource creation option can be used to specified "
1747 "the id of the root <Document> node. The default value is root_doc." ),
1748 QStringLiteral( "root_doc" ) // Default value
1749 ) );
1750
1751 driverMetadata.insert( QStringLiteral( "KML" ),
1753 QStringLiteral( "Keyhole Markup Language [KML]" ),
1754 QObject::tr( "Keyhole Markup Language [KML]" ),
1755 QStringLiteral( "*.kml" ),
1756 QStringLiteral( "kml" ),
1757 datasetOptions,
1758 layerOptions,
1759 QStringLiteral( "UTF-8" )
1760 )
1761 );
1762
1763 // Mapinfo
1764 datasetOptions.clear();
1765 layerOptions.clear();
1766
1767 auto insertMapInfoOptions = []( QMap<QString, QgsVectorFileWriter::Option *> &datasetOptions, QMap<QString, QgsVectorFileWriter::Option *> &layerOptions )
1768 {
1769 datasetOptions.insert( QStringLiteral( "SPATIAL_INDEX_MODE" ), new QgsVectorFileWriter::SetOption(
1770 QObject::tr( "Use this to turn on 'quick spatial index mode'. "
1771 "In this mode writing files can be about 5 times faster, "
1772 "but spatial queries can be up to 30 times slower." ),
1773 QStringList()
1774 << QStringLiteral( "QUICK" )
1775 << QStringLiteral( "OPTIMIZED" ),
1776 QStringLiteral( "QUICK" ), // Default value
1777 true // Allow None
1778 ) );
1779
1780 datasetOptions.insert( QStringLiteral( "BLOCK_SIZE" ), new QgsVectorFileWriter::IntOption(
1781 QObject::tr( "(multiples of 512): Block size for .map files. Defaults "
1782 "to 512. MapInfo 15.2 and above creates .tab files with a "
1783 "blocksize of 16384 bytes. Any MapInfo version should be "
1784 "able to handle block sizes from 512 to 32256." ),
1785 512
1786 ) );
1787 layerOptions.insert( QStringLiteral( "BOUNDS" ), new QgsVectorFileWriter::StringOption(
1788 QObject::tr( "xmin,ymin,xmax,ymax: Define custom layer bounds to increase the "
1789 "accuracy of the coordinates. Note: the geometry of written "
1790 "features must be within the defined box." ),
1791 QString() // Default value
1792 ) );
1793 };
1794 insertMapInfoOptions( datasetOptions, layerOptions );
1795
1796 driverMetadata.insert( QStringLiteral( "MapInfo File" ),
1798 QStringLiteral( "Mapinfo" ),
1799 QObject::tr( "Mapinfo TAB" ),
1800 QStringLiteral( "*.tab" ),
1801 QStringLiteral( "tab" ),
1802 datasetOptions,
1803 layerOptions
1804 )
1805 );
1806 datasetOptions.clear();
1807 layerOptions.clear();
1808 insertMapInfoOptions( datasetOptions, layerOptions );
1809
1810 // QGIS internal alias for MIF files
1811 driverMetadata.insert( QStringLiteral( "MapInfo MIF" ),
1813 QStringLiteral( "Mapinfo" ),
1814 QObject::tr( "Mapinfo MIF" ),
1815 QStringLiteral( "*.mif" ),
1816 QStringLiteral( "mif" ),
1817 datasetOptions,
1818 layerOptions
1819 )
1820 );
1821
1822 // Microstation DGN
1823 datasetOptions.clear();
1824 layerOptions.clear();
1825
1826 datasetOptions.insert( QStringLiteral( "3D" ), new QgsVectorFileWriter::BoolOption(
1827 QObject::tr( "Determine whether 2D (seed_2d.dgn) or 3D (seed_3d.dgn) "
1828 "seed file should be used. This option is ignored if the SEED option is provided." ),
1829 false // Default value
1830 ) );
1831
1832 datasetOptions.insert( QStringLiteral( "SEED" ), new QgsVectorFileWriter::StringOption(
1833 QObject::tr( "Override the seed file to use." ),
1834 QString() // Default value
1835 ) );
1836
1837 datasetOptions.insert( QStringLiteral( "COPY_WHOLE_SEED_FILE" ), new QgsVectorFileWriter::BoolOption(
1838 QObject::tr( "Indicate whether the whole seed file should be copied. "
1839 "If not, only the first three elements will be copied." ),
1840 false // Default value
1841 ) );
1842
1843 datasetOptions.insert( QStringLiteral( "COPY_SEED_FILE_COLOR_TABLE" ), new QgsVectorFileWriter::BoolOption(
1844 QObject::tr( "Indicates whether the color table should be copied from the seed file." ),
1845 false // Default value
1846 ) );
1847
1848 datasetOptions.insert( QStringLiteral( "MASTER_UNIT_NAME" ), new QgsVectorFileWriter::StringOption(
1849 QObject::tr( "Override the master unit name from the seed file with "
1850 "the provided one or two character unit name." ),
1851 QString() // Default value
1852 ) );
1853
1854 datasetOptions.insert( QStringLiteral( "SUB_UNIT_NAME" ), new QgsVectorFileWriter::StringOption(
1855 QObject::tr( "Override the sub unit name from the seed file with the provided "
1856 "one or two character unit name." ),
1857 QString() // Default value
1858 ) );
1859
1860 datasetOptions.insert( QStringLiteral( "SUB_UNITS_PER_MASTER_UNIT" ), new QgsVectorFileWriter::IntOption(
1861 QObject::tr( "Override the number of subunits per master unit. "
1862 "By default the seed file value is used." ),
1863 0 // Default value
1864 ) );
1865
1866 datasetOptions.insert( QStringLiteral( "UOR_PER_SUB_UNIT" ), new QgsVectorFileWriter::IntOption(
1867 QObject::tr( "Override the number of UORs (Units of Resolution) "
1868 "per sub unit. By default the seed file value is used." ),
1869 0 // Default value
1870 ) );
1871
1872 datasetOptions.insert( QStringLiteral( "ORIGIN" ), new QgsVectorFileWriter::StringOption(
1873 QObject::tr( "ORIGIN=x,y,z: Override the origin of the design plane. "
1874 "By default the origin from the seed file is used." ),
1875 QString() // Default value
1876 ) );
1877
1878 driverMetadata.insert( QStringLiteral( "DGN" ),
1880 QStringLiteral( "Microstation DGN" ),
1881 QObject::tr( "Microstation DGN" ),
1882 QStringLiteral( "*.dgn" ),
1883 QStringLiteral( "dgn" ),
1884 datasetOptions,
1885 layerOptions
1886 )
1887 );
1888
1889 // S-57 Base file
1890 datasetOptions.clear();
1891 layerOptions.clear();
1892
1893 datasetOptions.insert( QStringLiteral( "UPDATES" ), new QgsVectorFileWriter::SetOption(
1894 QObject::tr( "Should update files be incorporated into the base data on the fly." ),
1895 QStringList()
1896 << QStringLiteral( "APPLY" )
1897 << QStringLiteral( "IGNORE" ),
1898 QStringLiteral( "APPLY" ) // Default value
1899 ) );
1900
1901 datasetOptions.insert( QStringLiteral( "SPLIT_MULTIPOINT" ), new QgsVectorFileWriter::BoolOption(
1902 QObject::tr( "Should multipoint soundings be split into many single point sounding features. "
1903 "Multipoint geometries are not well handled by many formats, "
1904 "so it can be convenient to split single sounding features with many points "
1905 "into many single point features." ),
1906 false // Default value
1907 ) );
1908
1909 datasetOptions.insert( QStringLiteral( "ADD_SOUNDG_DEPTH" ), new QgsVectorFileWriter::BoolOption(
1910 QObject::tr( "Should a DEPTH attribute be added on SOUNDG features and assign the depth "
1911 "of the sounding. This should only be enabled when SPLIT_MULTIPOINT is "
1912 "also enabled." ),
1913 false // Default value
1914 ) );
1915
1916 datasetOptions.insert( QStringLiteral( "RETURN_PRIMITIVES" ), new QgsVectorFileWriter::BoolOption(
1917 QObject::tr( "Should all the low level geometry primitives be returned as special "
1918 "IsolatedNode, ConnectedNode, Edge and Face layers." ),
1919 false // Default value
1920 ) );
1921
1922 datasetOptions.insert( QStringLiteral( "PRESERVE_EMPTY_NUMBERS" ), new QgsVectorFileWriter::BoolOption(
1923 QObject::tr( "If enabled, numeric attributes assigned an empty string as a value will "
1924 "be preserved as a special numeric value. This option should not generally "
1925 "be needed, but may be useful when translated S-57 to S-57 losslessly." ),
1926 false // Default value
1927 ) );
1928
1929 datasetOptions.insert( QStringLiteral( "LNAM_REFS" ), new QgsVectorFileWriter::BoolOption(
1930 QObject::tr( "Should LNAM and LNAM_REFS fields be attached to features capturing "
1931 "the feature to feature relationships in the FFPT group of the S-57 file." ),
1932 true // Default value
1933 ) );
1934
1935 datasetOptions.insert( QStringLiteral( "RETURN_LINKAGES" ), new QgsVectorFileWriter::BoolOption(
1936 QObject::tr( "Should additional attributes relating features to their underlying "
1937 "geometric primitives be attached. These are the values of the FSPT group, "
1938 "and are primarily needed when doing S-57 to S-57 translations." ),
1939 false // Default value
1940 ) );
1941
1942 datasetOptions.insert( QStringLiteral( "RECODE_BY_DSSI" ), new QgsVectorFileWriter::BoolOption(
1943 QObject::tr( "Should attribute values be recoded to UTF-8 from the character encoding "
1944 "specified in the S57 DSSI record." ),
1945 false // Default value
1946 ) );
1947
1948 // set OGR_S57_OPTIONS = "RETURN_PRIMITIVES=ON,RETURN_LINKAGES=ON,LNAM_REFS=ON"
1949
1950 driverMetadata.insert( QStringLiteral( "S57" ),
1952 QStringLiteral( "S-57 Base file" ),
1953 QObject::tr( "S-57 Base file" ),
1954 QStringLiteral( "*.000" ),
1955 QStringLiteral( "000" ),
1956 datasetOptions,
1957 layerOptions
1958 )
1959 );
1960
1961 // Spatial Data Transfer Standard [SDTS]
1962 datasetOptions.clear();
1963 layerOptions.clear();
1964
1965 driverMetadata.insert( QStringLiteral( "SDTS" ),
1967 QStringLiteral( "Spatial Data Transfer Standard [SDTS]" ),
1968 QObject::tr( "Spatial Data Transfer Standard [SDTS]" ),
1969 QStringLiteral( "*catd.ddf" ),
1970 QStringLiteral( "ddf" ),
1971 datasetOptions,
1972 layerOptions
1973 )
1974 );
1975
1976 // SQLite
1977 datasetOptions.clear();
1978 layerOptions.clear();
1979
1980 datasetOptions.insert( QStringLiteral( "METADATA" ), new QgsVectorFileWriter::BoolOption(
1981 QObject::tr( "Can be used to avoid creating the geometry_columns and spatial_ref_sys "
1982 "tables in a new database. By default these metadata tables are created "
1983 "when a new database is created." ),
1984 true // Default value
1985 ) );
1986
1987 // Will handle the SpatiaLite alias
1988 datasetOptions.insert( QStringLiteral( "SPATIALITE" ), new QgsVectorFileWriter::HiddenOption(
1989 QStringLiteral( "NO" )
1990 ) );
1991
1992
1993 datasetOptions.insert( QStringLiteral( "INIT_WITH_EPSG" ), new QgsVectorFileWriter::HiddenOption(
1994 QStringLiteral( "NO" )
1995 ) );
1996
1997 layerOptions.insert( QStringLiteral( "FORMAT" ), new QgsVectorFileWriter::SetOption(
1998 QObject::tr( "Controls the format used for the geometry column. Defaults to WKB. "
1999 "This is generally more space and processing efficient, but harder "
2000 "to inspect or use in simple applications than WKT (Well Known Text)." ),
2001 QStringList()
2002 << QStringLiteral( "WKB" )
2003 << QStringLiteral( "WKT" ),
2004 QStringLiteral( "WKB" ) // Default value
2005 ) );
2006
2007 layerOptions.insert( QStringLiteral( "LAUNDER" ), new QgsVectorFileWriter::BoolOption(
2008 QObject::tr( "Controls whether layer and field names will be laundered for easier use "
2009 "in SQLite. Laundered names will be converted to lower case and some special "
2010 "characters(' - #) will be changed to underscores." ),
2011 true // Default value
2012 ) );
2013
2014 layerOptions.insert( QStringLiteral( "SPATIAL_INDEX" ), new QgsVectorFileWriter::HiddenOption(
2015 QStringLiteral( "NO" )
2016 ) );
2017
2018 layerOptions.insert( QStringLiteral( "COMPRESS_GEOM" ), new QgsVectorFileWriter::HiddenOption(
2019 QStringLiteral( "NO" )
2020 ) );
2021
2022 layerOptions.insert( QStringLiteral( "SRID" ), new QgsVectorFileWriter::HiddenOption(
2023 QString()
2024 ) );
2025
2026 layerOptions.insert( QStringLiteral( "COMPRESS_COLUMNS" ), new QgsVectorFileWriter::StringOption(
2027 QObject::tr( "column_name1[,column_name2, …] A list of (String) columns that "
2028 "must be compressed with ZLib DEFLATE algorithm. This might be beneficial "
2029 "for databases that have big string blobs. However, use with care, since "
2030 "the value of such columns will be seen as compressed binary content with "
2031 "other SQLite utilities (or previous OGR versions). With OGR, when inserting, "
2032 "modifying or querying compressed columns, compression/decompression is "
2033 "done transparently. However, such columns cannot be (easily) queried with "
2034 "an attribute filter or WHERE clause. Note: in table definition, such columns "
2035 "have the 'VARCHAR_deflate' declaration type." ),
2036 QString() // Default value
2037 ) );
2038
2039 driverMetadata.insert( QStringLiteral( "SQLite" ),
2041 QStringLiteral( "SQLite" ),
2042 QObject::tr( "SQLite" ),
2043 QStringLiteral( "*.sqlite" ),
2044 QStringLiteral( "sqlite" ),
2045 datasetOptions,
2046 layerOptions,
2047 QStringLiteral( "UTF-8" )
2048 )
2049 );
2050
2051 // SpatiaLite
2052 datasetOptions.clear();
2053 layerOptions.clear();
2054
2055 datasetOptions.insert( QStringLiteral( "METADATA" ), new QgsVectorFileWriter::BoolOption(
2056 QObject::tr( "Can be used to avoid creating the geometry_columns and spatial_ref_sys "
2057 "tables in a new database. By default these metadata tables are created "
2058 "when a new database is created." ),
2059 true // Default value
2060 ) );
2061
2062 datasetOptions.insert( QStringLiteral( "SPATIALITE" ), new QgsVectorFileWriter::HiddenOption(
2063 QStringLiteral( "YES" )
2064 ) );
2065
2066 datasetOptions.insert( QStringLiteral( "INIT_WITH_EPSG" ), new QgsVectorFileWriter::BoolOption(
2067 QObject::tr( "Insert the content of the EPSG CSV files into the spatial_ref_sys table. "
2068 "Set to NO for regular SQLite databases." ),
2069 true // Default value
2070 ) );
2071
2072 layerOptions.insert( QStringLiteral( "FORMAT" ), new QgsVectorFileWriter::HiddenOption(
2073 QStringLiteral( "SPATIALITE" )
2074 ) );
2075
2076 layerOptions.insert( QStringLiteral( "LAUNDER" ), new QgsVectorFileWriter::BoolOption(
2077 QObject::tr( "Controls whether layer and field names will be laundered for easier use "
2078 "in SQLite. Laundered names will be converted to lower case and some special "
2079 "characters(' - #) will be changed to underscores." ),
2080 true // Default value
2081 ) );
2082
2083 layerOptions.insert( QStringLiteral( "SPATIAL_INDEX" ), new QgsVectorFileWriter::BoolOption(
2084 QObject::tr( "If the database is of the SpatiaLite flavor, and if OGR is linked "
2085 "against libspatialite, this option can be used to control if a spatial "
2086 "index must be created." ),
2087 true // Default value
2088 ) );
2089
2090 layerOptions.insert( QStringLiteral( "COMPRESS_GEOM" ), new QgsVectorFileWriter::BoolOption(
2091 QObject::tr( "If the format of the geometry BLOB is of the SpatiaLite flavor, "
2092 "this option can be used to control if the compressed format for "
2093 "geometries (LINESTRINGs, POLYGONs) must be used." ),
2094 false // Default value
2095 ) );
2096
2097 layerOptions.insert( QStringLiteral( "SRID" ), new QgsVectorFileWriter::StringOption(
2098 QObject::tr( "Used to force the SRID number of the SRS associated with the layer. "
2099 "When this option isn't specified and that a SRS is associated with the "
2100 "layer, a search is made in the spatial_ref_sys to find a match for the "
2101 "SRS, and, if there is no match, a new entry is inserted for the SRS in "
2102 "the spatial_ref_sys table. When the SRID option is specified, this "
2103 "search (and the eventual insertion of a new entry) will not be done: "
2104 "the specified SRID is used as such." ),
2105 QString() // Default value
2106 ) );
2107
2108 layerOptions.insert( QStringLiteral( "COMPRESS_COLUMNS" ), new QgsVectorFileWriter::StringOption(
2109 QObject::tr( "column_name1[,column_name2, …] A list of (String) columns that "
2110 "must be compressed with ZLib DEFLATE algorithm. This might be beneficial "
2111 "for databases that have big string blobs. However, use with care, since "
2112 "the value of such columns will be seen as compressed binary content with "
2113 "other SQLite utilities (or previous OGR versions). With OGR, when inserting, "
2114 "modifying or queryings compressed columns, compression/decompression is "
2115 "done transparently. However, such columns cannot be (easily) queried with "
2116 "an attribute filter or WHERE clause. Note: in table definition, such columns "
2117 "have the 'VARCHAR_deflate' declaration type." ),
2118 QString() // Default value
2119 ) );
2120
2121 driverMetadata.insert( QStringLiteral( "SpatiaLite" ),
2123 QStringLiteral( "SpatiaLite" ),
2124 QObject::tr( "SpatiaLite" ),
2125 QStringLiteral( "*.sqlite" ),
2126 QStringLiteral( "sqlite" ),
2127 datasetOptions,
2128 layerOptions,
2129 QStringLiteral( "UTF-8" )
2130 )
2131 );
2132 // AutoCAD DXF
2133 datasetOptions.clear();
2134 layerOptions.clear();
2135
2136 datasetOptions.insert( QStringLiteral( "HEADER" ), new QgsVectorFileWriter::StringOption(
2137 QObject::tr( "Override the header file used - in place of header.dxf." ),
2138 QString() // Default value
2139 ) );
2140
2141 datasetOptions.insert( QStringLiteral( "TRAILER" ), new QgsVectorFileWriter::StringOption(
2142 QObject::tr( "Override the trailer file used - in place of trailer.dxf." ),
2143 QString() // Default value
2144 ) );
2145
2146 driverMetadata.insert( QStringLiteral( "DXF" ),
2148 QStringLiteral( "AutoCAD DXF" ),
2149 QObject::tr( "AutoCAD DXF" ),
2150 QStringLiteral( "*.dxf" ),
2151 QStringLiteral( "dxf" ),
2152 datasetOptions,
2153 layerOptions
2154 )
2155 );
2156
2157 // Geoconcept
2158 datasetOptions.clear();
2159 layerOptions.clear();
2160
2161 datasetOptions.insert( QStringLiteral( "EXTENSION" ), new QgsVectorFileWriter::SetOption(
2162 QObject::tr( "Indicates the GeoConcept export file extension. "
2163 "TXT was used by earlier releases of GeoConcept. GXT is currently used." ),
2164 QStringList()
2165 << QStringLiteral( "GXT" )
2166 << QStringLiteral( "TXT" ),
2167 QStringLiteral( "GXT" ) // Default value
2168 ) );
2169
2170 datasetOptions.insert( QStringLiteral( "CONFIG" ), new QgsVectorFileWriter::StringOption(
2171 QObject::tr( "Path to the GCT: the GCT file describes the GeoConcept types definitions: "
2172 "In this file, every line must start with //# followed by a keyword. "
2173 "Lines starting with // are comments." ),
2174 QString() // Default value
2175 ) );
2176
2177 datasetOptions.insert( QStringLiteral( "FEATURETYPE" ), new QgsVectorFileWriter::StringOption(
2178 QObject::tr( "Defines the feature to be created. The TYPE corresponds to one of the Name "
2179 "found in the GCT file for a type section. The SUBTYPE corresponds to one of "
2180 "the Name found in the GCT file for a sub-type section within the previous "
2181 "type section." ),
2182 QString() // Default value
2183 ) );
2184
2185 driverMetadata.insert( QStringLiteral( "Geoconcept" ),
2187 QStringLiteral( "Geoconcept" ),
2188 QObject::tr( "Geoconcept" ),
2189 QStringLiteral( "*.gxt *.txt" ),
2190 QStringLiteral( "gxt" ),
2191 datasetOptions,
2192 layerOptions
2193 )
2194 );
2195
2196 // ESRI OpenFileGDB
2197 datasetOptions.clear();
2198 layerOptions.clear();
2199
2200#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,9,0)
2201 layerOptions.insert( QStringLiteral( "TARGET_ARCGIS_VERSION" ), new QgsVectorFileWriter::SetOption(
2202 QObject::tr( "Selects which ArcGIS version this dataset should be compatible with. ALL is used by default and means any ArcGIS 10.x or ArcGIS Pro version. Using ARCGIS_PRO_3_2_OR_LATER is required to export 64-bit integer fields as such, otherwise they will be converted as Real fields. ARCGIS_PRO_3_2_OR_LATER also supports proper Date and Time field types." ),
2203 QStringList()
2204 << QStringLiteral( "ALL" )
2205 << QStringLiteral( "ARCGIS_PRO_3_2_OR_LATER" ),
2206 QStringLiteral( "ALL" ) // Default value
2207 ) );
2208#endif
2209
2210 layerOptions.insert( QStringLiteral( "FEATURE_DATASET" ), new QgsVectorFileWriter::StringOption(
2211 QObject::tr( "When this option is set, the new layer will be created inside the named "
2212 "FeatureDataset folder. If the folder does not already exist, it will be created." ),
2213 QString() // Default value
2214 ) );
2215
2216 layerOptions.insert( QStringLiteral( "LAYER_ALIAS" ), new QgsVectorFileWriter::StringOption(
2217 QObject::tr( "Set layer name alias." ),
2218 QString() // Default value
2219 ) );
2220
2221 layerOptions.insert( QStringLiteral( "GEOMETRY_NAME" ), new QgsVectorFileWriter::StringOption(
2222 QObject::tr( "Set name of geometry column in new layer. Defaults to 'SHAPE'." ),
2223 QStringLiteral( "SHAPE" ) // Default value
2224 ) );
2225
2226 layerOptions.insert( QStringLiteral( "GEOMETRY_NULLABLE" ), new QgsVectorFileWriter::BoolOption(
2227 QObject::tr( "Whether the values of the geometry column can be NULL. Can be set to NO so that geometry is required. Default to 'YES'." ),
2228 true // Default value
2229 ) );
2230
2231 layerOptions.insert( QStringLiteral( "FID" ), new QgsVectorFileWriter::StringOption(
2232 QObject::tr( "Name of the OID column to create. Defaults to 'OBJECTID'." ),
2233 QStringLiteral( "OBJECTID" ) // Default value
2234 ) );
2235
2236 // TODO missing options -- requires double option type
2237 // XYTOLERANCE
2238 // ZTOLERANCE
2239 // MTOLERANCE
2240 // XORIGIN
2241 // YORIGIN
2242 // ZORIGIN
2243 // MORIGIN
2244 // XYSCALE
2245 // ZSCALE
2246 // ZORIGIN
2247
2248 layerOptions.insert( QStringLiteral( "COLUMN_TYPES" ), new QgsVectorFileWriter::StringOption(
2249 QObject::tr( "A list of strings of format field_name=fgdb_field_type (separated by comma) to force the FileGDB column type of fields to be created." ),
2250 QString( ) // Default value
2251 ) );
2252
2253 layerOptions.insert( QStringLiteral( "DOCUMENTATION" ), new QgsVectorFileWriter::StringOption(
2254 QObject::tr( "XML documentation for the layer." ),
2255 QString( ) // Default value
2256 ) );
2257 layerOptions.insert( QStringLiteral( "CONFIGURATION_KEYWORD" ), new QgsVectorFileWriter::SetOption(
2258 QObject::tr( "Customize how data is stored. By default text in UTF-8 and data up to 1TB." ),
2259 {QStringLiteral( "DEFAULTS" ), QStringLiteral( "MAX_FILE_SIZE_4GB" ), QStringLiteral( "MAX_FILE_SIZE_256TB" )},
2260 QStringLiteral( "DEFAULTS" ), // Default value
2261 false // Allow None
2262 ) );
2263
2264 layerOptions.insert( QStringLiteral( "CREATE_SHAPE_AREA_AND_LENGTH_FIELDS" ), new QgsVectorFileWriter::BoolOption(
2265 QObject::tr( " Defaults to NO (through CreateLayer() API). When this option is set, a Shape_Area and Shape_Length special fields will be created for polygonal layers (Shape_Length only for linear layers). These fields will automatically be populated with the feature’s area or length whenever a new feature is added to the dataset or an existing feature is amended. When using ogr2ogr with a source layer that has Shape_Area/Shape_Length special fields, and this option is not explicitly specified, it will be automatically set, so that the resulting FileGeodatabase has those fields properly tagged." ),
2266 false // Default value
2267 ) );
2268
2269 driverMetadata.insert( QStringLiteral( "OpenFileGDB" ),
2271 QStringLiteral( "ESRI File Geodatabase" ),
2272 QObject::tr( "ESRI File Geodatabase" ),
2273 QStringLiteral( "*.gdb" ),
2274 QStringLiteral( "gdb" ),
2275 datasetOptions,
2276 layerOptions,
2277 QStringLiteral( "UTF-8" )
2278 )
2279 );
2280
2281#if GDAL_VERSION_NUM < GDAL_COMPUTE_VERSION(3,11,0)
2282 // ESRI FileGDB (using ESRI FileGDB API SDK)
2283 datasetOptions.clear();
2284 layerOptions.clear();
2285
2286 layerOptions.insert( QStringLiteral( "FEATURE_DATASET" ), new QgsVectorFileWriter::StringOption(
2287 QObject::tr( "When this option is set, the new layer will be created inside the named "
2288 "FeatureDataset folder. If the folder does not already exist, it will be created." ),
2289 QString() // Default value
2290 ) );
2291
2292 layerOptions.insert( QStringLiteral( "GEOMETRY_NAME" ), new QgsVectorFileWriter::StringOption(
2293 QObject::tr( "Set name of geometry column in new layer. Defaults to 'SHAPE'." ),
2294 QStringLiteral( "SHAPE" ) // Default value
2295 ) );
2296
2297 layerOptions.insert( QStringLiteral( "FID" ), new QgsVectorFileWriter::StringOption(
2298 QObject::tr( "Name of the OID column to create. Defaults to 'OBJECTID'." ),
2299 QStringLiteral( "OBJECTID" ) // Default value
2300 ) );
2301
2302 driverMetadata.insert( QStringLiteral( "FileGDB" ),
2304 QStringLiteral( "ESRI FileGDB" ),
2305 QObject::tr( "ESRI FileGDB" ),
2306 QStringLiteral( "*.gdb" ),
2307 QStringLiteral( "gdb" ),
2308 datasetOptions,
2309 layerOptions,
2310 QStringLiteral( "UTF-8" )
2311 )
2312 );
2313#endif
2314
2315 // XLSX
2316 datasetOptions.clear();
2317 layerOptions.clear();
2318
2319 layerOptions.insert( QStringLiteral( "OGR_XLSX_FIELD_TYPES" ), new QgsVectorFileWriter::SetOption(
2320 QObject::tr( "By default, the driver will try to detect the data type of fields. If set "
2321 "to STRING, all fields will be of String type." ),
2322 QStringList()
2323 << QStringLiteral( "AUTO" )
2324 << QStringLiteral( "STRING" ),
2325 QStringLiteral( "AUTO" ), // Default value
2326 false // Allow None
2327 ) );
2328
2329 layerOptions.insert( QStringLiteral( "OGR_XLSX_HEADERS" ), new QgsVectorFileWriter::SetOption(
2330 QObject::tr( "By default, the driver will read the first lines of each sheet to detect "
2331 "if the first line might be the name of columns. If set to FORCE, the driver "
2332 "will consider the first line as the header line. If set to "
2333 "DISABLE, it will be considered as the first feature. Otherwise "
2334 "auto-detection will occur." ),
2335 QStringList()
2336 << QStringLiteral( "FORCE" )
2337 << QStringLiteral( "DISABLE" )
2338 << QStringLiteral( "AUTO" ),
2339 QStringLiteral( "AUTO" ), // Default value
2340 false // Allow None
2341 ) );
2342
2343 driverMetadata.insert( QStringLiteral( "XLSX" ),
2345 QStringLiteral( "MS Office Open XML spreadsheet" ),
2346 QObject::tr( "MS Office Open XML spreadsheet [XLSX]" ),
2347 QStringLiteral( "*.xlsx" ),
2348 QStringLiteral( "xlsx" ),
2349 datasetOptions,
2350 layerOptions,
2351 QStringLiteral( "UTF-8" )
2352 )
2353 );
2354
2355 // ODS
2356 datasetOptions.clear();
2357 layerOptions.clear();
2358
2359 layerOptions.insert( QStringLiteral( "OGR_ODS_FIELD_TYPES" ), new QgsVectorFileWriter::SetOption(
2360 QObject::tr( "By default, the driver will try to detect the data type of fields. If set "
2361 "to STRING, all fields will be of String type." ),
2362 QStringList()
2363 << QStringLiteral( "AUTO" )
2364 << QStringLiteral( "STRING" ),
2365 QStringLiteral( "AUTO" ), // Default value
2366 false // Allow None
2367 ) );
2368
2369 layerOptions.insert( QStringLiteral( "OGR_ODS_HEADERS" ), new QgsVectorFileWriter::SetOption(
2370 QObject::tr( "By default, the driver will read the first lines of each sheet to detect "
2371 "if the first line might be the name of columns. If set to FORCE, the driver "
2372 "will consider the first line as the header line. If set to "
2373 "DISABLE, it will be considered as the first feature. Otherwise "
2374 "auto-detection will occur." ),
2375 QStringList()
2376 << QStringLiteral( "FORCE" )
2377 << QStringLiteral( "DISABLE" )
2378 << QStringLiteral( "AUTO" ),
2379 QStringLiteral( "AUTO" ), // Default value
2380 false // Allow None
2381 ) );
2382
2383 driverMetadata.insert( QStringLiteral( "ODS" ),
2385 QStringLiteral( "Open Document Spreadsheet" ),
2386 QObject::tr( "Open Document Spreadsheet [ODS]" ),
2387 QStringLiteral( "*.ods" ),
2388 QStringLiteral( "ods" ),
2389 datasetOptions,
2390 layerOptions,
2391 QStringLiteral( "UTF-8" )
2392 )
2393 );
2394
2395 // Parquet
2396 datasetOptions.clear();
2397 layerOptions.clear();
2398
2399 layerOptions.insert( QStringLiteral( "COMPRESSION" ), new QgsVectorFileWriter::SetOption(
2400 QObject::tr( "Compression method." ),
2401 QStringList()
2402 << QStringLiteral( "UNCOMPRESSED" )
2403 << QStringLiteral( "SNAPPY" ),
2404 QStringLiteral( "SNAPPY" ), // Default value
2405 false // Allow None
2406 ) );
2407
2408 layerOptions.insert( QStringLiteral( "GEOMETRY_ENCODING" ), new QgsVectorFileWriter::SetOption(
2409 QObject::tr( "Geometry encoding." ),
2410 QStringList()
2411 << QStringLiteral( "WKB" )
2412 << QStringLiteral( "WKT" )
2413 << QStringLiteral( "GEOARROW" ),
2414 QStringLiteral( "WKB" ), // Default value
2415 false // Allow None
2416 ) );
2417
2418 layerOptions.insert( QStringLiteral( "ROW_GROUP_SIZE" ), new QgsVectorFileWriter::IntOption(
2419 QObject::tr( "Maximum number of rows per group." ),
2420 65536 // Default value
2421 ) );
2422
2423 layerOptions.insert( QStringLiteral( "FID" ), new QgsVectorFileWriter::StringOption(
2424 QObject::tr( "Name for the feature identifier column" ),
2425 QString() // Default value
2426 ) );
2427
2428 layerOptions.insert( QStringLiteral( "GEOMETRY_NAME" ), new QgsVectorFileWriter::StringOption(
2429 QObject::tr( "Name for the geometry column" ),
2430 QStringLiteral( "geometry" ) // Default value
2431 ) );
2432
2433 layerOptions.insert( QStringLiteral( "EDGES" ), new QgsVectorFileWriter::SetOption(
2434 QObject::tr( "Name of the coordinate system for the edges." ),
2435 QStringList()
2436 << QStringLiteral( "PLANAR" )
2437 << QStringLiteral( "SPHERICAL" ),
2438 QStringLiteral( "PLANAR" ), // Default value
2439 false // Allow None
2440 ) );
2441
2442 driverMetadata.insert( QStringLiteral( "Parquet" ),
2444 QStringLiteral( "(Geo)Parquet" ),
2445 QObject::tr( "(Geo)Parquet" ),
2446 QStringLiteral( "*.parquet" ),
2447 QStringLiteral( "parquet" ),
2448 datasetOptions,
2449 layerOptions,
2450 QStringLiteral( "UTF-8" )
2451 )
2452 );
2453
2454 // PGDump
2455 datasetOptions.clear();
2456 layerOptions.clear();
2457
2458 datasetOptions.insert( QStringLiteral( "LINEFORMAT" ), new QgsVectorFileWriter::SetOption(
2459 QObject::tr( "Line termination character sequence." ),
2460 QStringList()
2461 << QStringLiteral( "CRLF" )
2462 << QStringLiteral( "LF" ),
2463 QStringLiteral( "LF" ), // Default value
2464 false // Allow None
2465 ) );
2466
2467
2468 layerOptions.insert( QStringLiteral( "GEOM_TYPE" ), new QgsVectorFileWriter::SetOption(
2469 QObject::tr( "Format of geometry columns." ),
2470 QStringList()
2471 << QStringLiteral( "geometry" )
2472 << QStringLiteral( "geography" ),
2473 QStringLiteral( "geometry" ), // Default value
2474 false // Allow None
2475 ) );
2476
2477 layerOptions.insert( QStringLiteral( "LAUNDER" ), new QgsVectorFileWriter::BoolOption(
2478 QObject::tr( "Controls whether layer and field names will be laundered for easier use. "
2479 "Laundered names will be converted to lower case and some special "
2480 "characters(' - #) will be changed to underscores." ),
2481 true // Default value
2482 ) );
2483
2484 layerOptions.insert( QStringLiteral( "GEOMETRY_NAME" ), new QgsVectorFileWriter::StringOption(
2485 QObject::tr( "Name for the geometry column. Defaults to wkb_geometry "
2486 "for GEOM_TYPE=geometry or the_geog for GEOM_TYPE=geography" ) ) );
2487
2488 layerOptions.insert( QStringLiteral( "SCHEMA" ), new QgsVectorFileWriter::StringOption(
2489 QObject::tr( "Name of schema into which to create the new table" ) ) );
2490
2491 layerOptions.insert( QStringLiteral( "CREATE_SCHEMA" ), new QgsVectorFileWriter::BoolOption(
2492 QObject::tr( "Whether to explicitly emit the CREATE SCHEMA statement to create the specified schema." ),
2493 true // Default value
2494 ) );
2495
2496 layerOptions.insert( QStringLiteral( "CREATE_TABLE" ), new QgsVectorFileWriter::BoolOption(
2497 QObject::tr( "Whether to explicitly recreate the table if necessary." ),
2498 true // Default value
2499 ) );
2500
2501 layerOptions.insert( QStringLiteral( "DROP_TABLE" ), new QgsVectorFileWriter::SetOption(
2502 QObject::tr( "Whether to explicitly destroy tables before recreating them." ),
2503 QStringList()
2504 << QStringLiteral( "YES" )
2505 << QStringLiteral( "NO" )
2506 << QStringLiteral( "IF_EXISTS" ),
2507 QStringLiteral( "YES" ), // Default value
2508 false // Allow None
2509 ) );
2510
2511 layerOptions.insert( QStringLiteral( "SRID" ), new QgsVectorFileWriter::StringOption(
2512 QObject::tr( "Used to force the SRID number of the SRS associated with the layer. "
2513 "When this option isn't specified and that a SRS is associated with the "
2514 "layer, a search is made in the spatial_ref_sys to find a match for the "
2515 "SRS, and, if there is no match, a new entry is inserted for the SRS in "
2516 "the spatial_ref_sys table. When the SRID option is specified, this "
2517 "search (and the eventual insertion of a new entry) will not be done: "
2518 "the specified SRID is used as such." ),
2519 QString() // Default value
2520 ) );
2521
2522 layerOptions.insert( QStringLiteral( "POSTGIS_VERSION" ), new QgsVectorFileWriter::StringOption(
2523 QObject::tr( "Can be set to 2.0 or 2.2 for PostGIS 2.0/2.2 compatibility. "
2524 "Important to set it correctly if using non-linear geometry types" ),
2525 QStringLiteral( "2.2" ) // Default value
2526 ) );
2527
2528 driverMetadata.insert( QStringLiteral( "PGDUMP" ),
2530 QStringLiteral( "PostgreSQL SQL dump" ),
2531 QObject::tr( "PostgreSQL SQL dump" ),
2532 QStringLiteral( "*.sql" ),
2533 QStringLiteral( "sql" ),
2534 datasetOptions,
2535 layerOptions,
2536 QStringLiteral( "UTF-8" )
2537 )
2538 );
2539
2540 }
2541
2542 QgsVectorFileWriterMetadataContainer( const QgsVectorFileWriterMetadataContainer &other ) = delete;
2543 QgsVectorFileWriterMetadataContainer &operator=( const QgsVectorFileWriterMetadataContainer &other ) = delete;
2544 ~QgsVectorFileWriterMetadataContainer()
2545 {
2546 for ( auto it = driverMetadata.constBegin(); it != driverMetadata.constEnd(); ++it )
2547 {
2548 for ( auto optionIt = it.value().driverOptions.constBegin(); optionIt != it.value().driverOptions.constEnd(); ++optionIt )
2549 delete optionIt.value();
2550 for ( auto optionIt = it.value().layerOptions.constBegin(); optionIt != it.value().layerOptions.constEnd(); ++optionIt )
2551 delete optionIt.value();
2552 }
2553 }
2554
2555 QMap<QString, QgsVectorFileWriter::MetaData> driverMetadata;
2556
2557};
2559
2560bool QgsVectorFileWriter::driverMetadata( const QString &driverName, QgsVectorFileWriter::MetaData &driverMetadata )
2561{
2562 static QgsVectorFileWriterMetadataContainer sDriverMetadata;
2563 QMap<QString, MetaData>::ConstIterator it = sDriverMetadata.driverMetadata.constBegin();
2564
2565 for ( ; it != sDriverMetadata.driverMetadata.constEnd(); ++it )
2566 {
2567 if ( it.key() == QLatin1String( "PGDUMP" ) &&
2568 driverName != QLatin1String( "PGDUMP" ) &&
2569 driverName != QLatin1String( "PostgreSQL SQL dump" ) )
2570 {
2571 // We do not want the 'PG' driver to be wrongly identified with PGDUMP
2572 continue;
2573 }
2574 if ( it.key().startsWith( driverName ) || it.value().longName.startsWith( driverName ) )
2575 {
2576 driverMetadata = it.value();
2577 return true;
2578 }
2579 }
2580
2581 return false;
2582}
2583
2584QStringList QgsVectorFileWriter::defaultDatasetOptions( const QString &driverName )
2585{
2586 MetaData metadata;
2587 bool ok = driverMetadata( driverName, metadata );
2588 if ( !ok )
2589 return QStringList();
2590 return concatenateOptions( metadata.driverOptions );
2591}
2592
2593QStringList QgsVectorFileWriter::defaultLayerOptions( const QString &driverName )
2594{
2595 MetaData metadata;
2596 bool ok = driverMetadata( driverName, metadata );
2597 if ( !ok )
2598 return QStringList();
2599 return concatenateOptions( metadata.layerOptions );
2600}
2601
2603{
2604
2605 OGRwkbGeometryType ogrType = static_cast<OGRwkbGeometryType>( type );
2606
2608 {
2609 ogrType = static_cast<OGRwkbGeometryType>( QgsWkbTypes::to25D( type ) );
2610 }
2611 return ogrType;
2612}
2613
2618
2620{
2621 return mErrorMessage;
2622}
2623
2625{
2626 return mOgrDriverName;
2627}
2628
2630{
2631 return mOgrDriverLongName;
2632}
2633
2635{
2636 return mCapabilities;
2637}
2638
2643
2645{
2646 QgsFeatureList::iterator fIt = features.begin();
2647 bool result = true;
2648 for ( ; fIt != features.end(); ++fIt )
2649 {
2650 result = result && addFeatureWithStyle( *fIt, nullptr, Qgis::DistanceUnit::Meters );
2651 }
2652 return result;
2653}
2654
2656{
2657 return mErrorMessage;
2658}
2659
2661{
2662 // create the feature
2663 gdal::ogr_feature_unique_ptr poFeature = createFeature( feature );
2664 if ( !poFeature )
2665 return false;
2666
2667 //add OGR feature style type
2669 {
2670 mRenderContext.expressionContext().setFeature( feature );
2671 //SymbolLayerSymbology: concatenate ogr styles of all symbollayers
2672 QgsSymbolList symbols = renderer->symbolsForFeature( feature, mRenderContext );
2673 QString styleString;
2674 QString currentStyle;
2675
2676 QgsSymbolList::const_iterator symbolIt = symbols.constBegin();
2677 for ( ; symbolIt != symbols.constEnd(); ++symbolIt )
2678 {
2679 int nSymbolLayers = ( *symbolIt )->symbolLayerCount();
2680 for ( int i = 0; i < nSymbolLayers; ++i )
2681 {
2682#if 0
2683 QMap< QgsSymbolLayer *, QString >::const_iterator it = mSymbolLayerTable.find( ( *symbolIt )->symbolLayer( i ) );
2684 if ( it == mSymbolLayerTable.constEnd() )
2685 {
2686 continue;
2687 }
2688#endif
2689 double mmsf = mmScaleFactor( mSymbologyScale, ( *symbolIt )->outputUnit(), outputUnit );
2690 double musf = mapUnitScaleFactor( mSymbologyScale, ( *symbolIt )->outputUnit(), outputUnit );
2691
2692 currentStyle = ( *symbolIt )->symbolLayer( i )->ogrFeatureStyle( mmsf, musf );//"@" + it.value();
2693
2694 switch ( mSymbologyExport )
2695 {
2697 {
2698 if ( symbolIt != symbols.constBegin() || i != 0 )
2699 {
2700 styleString.append( ';' );
2701 }
2702 styleString.append( currentStyle );
2703 break;
2704 }
2706 {
2707 OGR_F_SetStyleString( poFeature.get(), currentStyle.toLocal8Bit().constData() );
2708 if ( !writeFeature( mLayer, poFeature.get() ) )
2709 {
2710 return false;
2711 }
2712 break;
2713 }
2714
2716 break;
2717 }
2718 }
2719 }
2720 OGR_F_SetStyleString( poFeature.get(), styleString.toLocal8Bit().constData() );
2721 }
2722
2723 switch ( mSymbologyExport )
2724 {
2727 {
2728 if ( !writeFeature( mLayer, poFeature.get() ) )
2729 {
2730 return false;
2731 }
2732 break;
2733 }
2734
2736 break;
2737 }
2738
2739 return true;
2740}
2741
2742gdal::ogr_feature_unique_ptr QgsVectorFileWriter::createFeature( const QgsFeature &feature )
2743{
2744 QgsLocaleNumC l; // Make sure the decimal delimiter is a dot
2745 Q_UNUSED( l )
2746
2747 gdal::ogr_feature_unique_ptr poFeature( OGR_F_Create( OGR_L_GetLayerDefn( mLayer ) ) );
2748
2749 // attribute handling
2750 for ( QMap<int, int>::const_iterator it = mAttrIdxToOgrIdx.constBegin(); it != mAttrIdxToOgrIdx.constEnd(); ++it )
2751 {
2752 int fldIdx = it.key();
2753 int ogrField = it.value();
2754
2755 QVariant attrValue = feature.attribute( fldIdx );
2756 QgsField field = mFields.at( fldIdx );
2757
2758 if ( feature.isUnsetValue( fldIdx ) )
2759 {
2760 OGR_F_UnsetField( poFeature.get(), ogrField );
2761 continue;
2762 }
2763 else if ( QgsVariantUtils::isNull( attrValue ) )
2764 {
2765// Starting with GDAL 2.2, there are 2 concepts: unset fields and null fields
2766// whereas previously there was only unset fields. For a GeoJSON output,
2767// leaving a field unset will cause it to not appear at all in the output
2768// feature.
2769// When all features of a layer have a field unset, this would cause the
2770// field to not be present at all in the output, and thus on reading to
2771// have disappeared. #16812
2772#ifdef OGRNullMarker
2773 OGR_F_SetFieldNull( poFeature.get(), ogrField );
2774#endif
2775 continue;
2776 }
2777
2779 {
2780 field = mFieldValueConverter->fieldDefinition( field );
2781 attrValue = mFieldValueConverter->convert( fldIdx, attrValue );
2782 }
2783
2784 // Check type compatibility before passing attribute value to OGR
2785 QString errorMessage;
2786 if ( ! field.convertCompatible( attrValue, &errorMessage ) )
2787 {
2788 mErrorMessage = QObject::tr( "Error converting value (%1) for attribute field %2: %3" )
2789 .arg( feature.attribute( fldIdx ).toString(),
2790 mFields.at( fldIdx ).name(), errorMessage );
2791 QgsMessageLog::logMessage( mErrorMessage, QObject::tr( "OGR" ) );
2793 return nullptr;
2794 }
2795
2796 switch ( field.type() )
2797 {
2798 case QMetaType::Type::Int:
2799 OGR_F_SetFieldInteger( poFeature.get(), ogrField, attrValue.toInt() );
2800 break;
2801 case QMetaType::Type::LongLong:
2802 OGR_F_SetFieldInteger64( poFeature.get(), ogrField, attrValue.toLongLong() );
2803 break;
2804 case QMetaType::Type::Bool:
2805 OGR_F_SetFieldInteger( poFeature.get(), ogrField, attrValue.toInt() );
2806 break;
2807 case QMetaType::Type::QString:
2808 OGR_F_SetFieldString( poFeature.get(), ogrField, mCodec->fromUnicode( attrValue.toString() ).constData() );
2809 break;
2810 case QMetaType::Type::Double:
2811 OGR_F_SetFieldDouble( poFeature.get(), ogrField, attrValue.toDouble() );
2812 break;
2813 case QMetaType::Type::QDate:
2814 OGR_F_SetFieldDateTime( poFeature.get(), ogrField,
2815 attrValue.toDate().year(),
2816 attrValue.toDate().month(),
2817 attrValue.toDate().day(),
2818 0, 0, 0, 0 );
2819 break;
2820 case QMetaType::Type::QDateTime:
2821 if ( mOgrDriverName == QLatin1String( "ESRI Shapefile" ) )
2822 {
2823 OGR_F_SetFieldString( poFeature.get(), ogrField, mCodec->fromUnicode( attrValue.toDateTime().toString( QStringLiteral( "yyyy/MM/dd hh:mm:ss.zzz" ) ) ).constData() );
2824 }
2825 else
2826 {
2827 const QDateTime dt = attrValue.toDateTime();
2828 const QDate date = dt.date();
2829 const QTime time = dt.time();
2830 OGR_F_SetFieldDateTimeEx( poFeature.get(), ogrField,
2831 date.year(),
2832 date.month(),
2833 date.day(),
2834 time.hour(),
2835 time.minute(),
2836 static_cast<float>( time.second() + static_cast< double >( time.msec() ) / 1000 ),
2838 }
2839 break;
2840 case QMetaType::Type::QTime:
2841 if ( mOgrDriverName == QLatin1String( "ESRI Shapefile" ) )
2842 {
2843 OGR_F_SetFieldString( poFeature.get(), ogrField, mCodec->fromUnicode( attrValue.toString() ).constData() );
2844 }
2845 else
2846 {
2847 const QTime time = attrValue.toTime();
2848 OGR_F_SetFieldDateTimeEx( poFeature.get(), ogrField,
2849 0, 0, 0,
2850 time.hour(),
2851 time.minute(),
2852 static_cast<float>( time.second() + static_cast< double >( time.msec() ) / 1000 ),
2853 0 );
2854 }
2855 break;
2856
2857 case QMetaType::Type::QByteArray:
2858 {
2859 const QByteArray ba = attrValue.toByteArray();
2860 OGR_F_SetFieldBinary( poFeature.get(), ogrField, ba.size(), const_cast< GByte * >( reinterpret_cast< const GByte * >( ba.data() ) ) );
2861 break;
2862 }
2863
2864 case QMetaType::Type::UnknownType:
2865 break;
2866
2867 case QMetaType::Type::QStringList:
2868 {
2869 // handle GPKG conversion to JSON
2870 if ( mOgrDriverName == QLatin1String( "GPKG" ) )
2871 {
2872 const QJsonDocument doc = QJsonDocument::fromVariant( attrValue );
2873 QString jsonString;
2874 if ( !doc.isNull() )
2875 {
2876 jsonString = QString::fromUtf8( doc.toJson( QJsonDocument::Compact ).constData() );
2877 }
2878 OGR_F_SetFieldString( poFeature.get(), ogrField, mCodec->fromUnicode( jsonString.constData() ) );
2879 break;
2880 }
2881
2882 QStringList list = attrValue.toStringList();
2883 if ( mSupportedListSubTypes.contains( QMetaType::Type::QString ) )
2884 {
2885 int count = list.count();
2886 char **lst = new char *[count + 1];
2887 if ( count > 0 )
2888 {
2889 int pos = 0;
2890 for ( const QString &string : list )
2891 {
2892 lst[pos] = CPLStrdup( mCodec->fromUnicode( string ).data() );
2893 pos++;
2894 }
2895 }
2896 lst[count] = nullptr;
2897 OGR_F_SetFieldStringList( poFeature.get(), ogrField, lst );
2898 CSLDestroy( lst );
2899 }
2900 else
2901 {
2902 OGR_F_SetFieldString( poFeature.get(), ogrField, mCodec->fromUnicode( list.join( ',' ) ).constData() );
2903 }
2904 break;
2905 }
2906
2907 case QMetaType::Type::QVariantList:
2908 // handle GPKG conversion to JSON
2909 if ( mOgrDriverName == QLatin1String( "GPKG" ) )
2910 {
2911 const QJsonDocument doc = QJsonDocument::fromVariant( attrValue );
2912 QString jsonString;
2913 if ( !doc.isNull() )
2914 {
2915 jsonString = QString::fromUtf8( doc.toJson( QJsonDocument::Compact ).data() );
2916 }
2917 OGR_F_SetFieldString( poFeature.get(), ogrField, mCodec->fromUnicode( jsonString.constData() ) );
2918 break;
2919 }
2920
2921 // fall through to default for unsupported types
2922 if ( field.subType() == QMetaType::Type::QString )
2923 {
2924 QStringList list = attrValue.toStringList();
2925 if ( mSupportedListSubTypes.contains( QMetaType::Type::QString ) )
2926 {
2927 int count = list.count();
2928 char **lst = new char *[count + 1];
2929 if ( count > 0 )
2930 {
2931 int pos = 0;
2932 for ( const QString &string : list )
2933 {
2934 lst[pos] = CPLStrdup( mCodec->fromUnicode( string ).data() );
2935 pos++;
2936 }
2937 }
2938 lst[count] = nullptr;
2939 OGR_F_SetFieldStringList( poFeature.get(), ogrField, lst );
2940 CSLDestroy( lst );
2941 }
2942 else
2943 {
2944 OGR_F_SetFieldString( poFeature.get(), ogrField, mCodec->fromUnicode( list.join( ',' ) ).constData() );
2945 }
2946 break;
2947 }
2948 else if ( field.subType() == QMetaType::Type::Int )
2949 {
2950 const QVariantList list = attrValue.toList();
2951 if ( mSupportedListSubTypes.contains( QMetaType::Type::Int ) )
2952 {
2953 const int count = list.count();
2954 int *lst = new int[count];
2955 if ( count > 0 )
2956 {
2957 int pos = 0;
2958 for ( const QVariant &value : list )
2959 {
2960 lst[pos] = value.toInt();
2961 pos++;
2962 }
2963 }
2964 OGR_F_SetFieldIntegerList( poFeature.get(), ogrField, count, lst );
2965 delete [] lst;
2966 }
2967 else
2968 {
2969 QStringList strings;
2970 strings.reserve( list.size() );
2971 for ( const QVariant &value : list )
2972 {
2973 strings << QString::number( value.toInt() );
2974 }
2975 OGR_F_SetFieldString( poFeature.get(), ogrField, mCodec->fromUnicode( strings.join( ',' ) ).constData() );
2976 }
2977 break;
2978 }
2979 else if ( field.subType() == QMetaType::Type::Double )
2980 {
2981 const QVariantList list = attrValue.toList();
2982 if ( mSupportedListSubTypes.contains( QMetaType::Type::Double ) )
2983 {
2984 const int count = list.count();
2985 double *lst = new double[count];
2986 if ( count > 0 )
2987 {
2988 int pos = 0;
2989 for ( const QVariant &value : list )
2990 {
2991 lst[pos] = value.toDouble();
2992 pos++;
2993 }
2994 }
2995 OGR_F_SetFieldDoubleList( poFeature.get(), ogrField, count, lst );
2996 delete [] lst;
2997 }
2998 else
2999 {
3000 QStringList strings;
3001 strings.reserve( list.size() );
3002 for ( const QVariant &value : list )
3003 {
3004 strings << QString::number( value.toDouble() );
3005 }
3006 OGR_F_SetFieldString( poFeature.get(), ogrField, mCodec->fromUnicode( strings.join( ',' ) ).constData() );
3007 }
3008 break;
3009 }
3010 else if ( field.subType() == QMetaType::Type::LongLong )
3011 {
3012 const QVariantList list = attrValue.toList();
3013 if ( mSupportedListSubTypes.contains( QMetaType::Type::LongLong ) )
3014 {
3015 const int count = list.count();
3016 long long *lst = new long long[count];
3017 if ( count > 0 )
3018 {
3019 int pos = 0;
3020 for ( const QVariant &value : list )
3021 {
3022 lst[pos] = value.toLongLong();
3023 pos++;
3024 }
3025 }
3026 OGR_F_SetFieldInteger64List( poFeature.get(), ogrField, count, lst );
3027 delete [] lst;
3028 }
3029 else
3030 {
3031 QStringList strings;
3032 strings.reserve( list.size() );
3033 for ( const QVariant &value : list )
3034 {
3035 strings << QString::number( value.toLongLong() );
3036 }
3037 OGR_F_SetFieldString( poFeature.get(), ogrField, mCodec->fromUnicode( strings.join( ',' ) ).constData() );
3038 }
3039 break;
3040 }
3041 //intentional fall-through
3042 [[fallthrough]];
3043
3044 case QMetaType::Type::QVariantMap:
3045 {
3046 // handle GPKG conversion to JSON
3047 const char *pszDataSubTypes = GDALGetMetadataItem( OGRGetDriverByName( mOgrDriverName.toLocal8Bit().constData() ), GDAL_DMD_CREATIONFIELDDATASUBTYPES, nullptr );
3048 if ( pszDataSubTypes && strstr( pszDataSubTypes, "JSON" ) )
3049 {
3050 const QJsonDocument doc = QJsonDocument::fromVariant( attrValue );
3051 QString jsonString;
3052 if ( !doc.isNull() )
3053 {
3054 const QByteArray json { doc.toJson( QJsonDocument::Compact ) };
3055 jsonString = QString::fromUtf8( json.data() );
3056 }
3057 OGR_F_SetFieldString( poFeature.get(), ogrField, mCodec->fromUnicode( jsonString.constData() ) );
3058 break;
3059 }
3060 }
3061
3062 //intentional fall-through
3063 [[fallthrough]];
3064
3065
3066 default:
3067 mErrorMessage = QObject::tr( "Invalid variant type for field %1[%2]: received %3 with type %4" )
3068 .arg( mFields.at( fldIdx ).name() )
3069 .arg( ogrField )
3070 .arg( attrValue.typeName(),
3071 attrValue.toString() );
3072 QgsMessageLog::logMessage( mErrorMessage, QObject::tr( "OGR" ) );
3074 return nullptr;
3075 }
3076 }
3077
3079 {
3080 if ( feature.hasGeometry() )
3081 {
3082 // build geometry from WKB
3083 QgsGeometry geom = feature.geometry();
3084 if ( mCoordinateTransform )
3085 {
3086 // output dataset requires coordinate transform
3087 try
3088 {
3089 geom.transform( *mCoordinateTransform );
3090 }
3091 catch ( QgsCsException & )
3092 {
3093 QgsLogger::warning( QObject::tr( "Feature geometry failed to transform" ) );
3094 return nullptr;
3095 }
3096 }
3097
3098 // turn single geometry to multi geometry if needed
3101 {
3102 geom.convertToMultiType();
3103 }
3104
3105 if ( geom.wkbType() != mWkbType )
3106 {
3107 OGRGeometryH mGeom2 = nullptr;
3108
3109 // If requested WKB type is 25D and geometry WKB type is 3D,
3110 // we must force the use of 25D.
3112 {
3113 //ND: I suspect there's a bug here, in that this is NOT converting the geometry's WKB type,
3114 //so the exported WKB has a different type to what the OGRGeometry is expecting.
3115 //possibly this is handled already in OGR, but it should be fixed regardless by actually converting
3116 //geom to the correct WKB type
3117 Qgis::WkbType wkbType = geom.wkbType();
3118 if ( wkbType >= Qgis::WkbType::PointZ && wkbType <= Qgis::WkbType::MultiPolygonZ )
3119 {
3120 Qgis::WkbType wkbType25d = static_cast<Qgis::WkbType>( static_cast< quint32>( geom.wkbType() ) - static_cast< quint32>( Qgis::WkbType::PointZ ) + static_cast<quint32>( Qgis::WkbType::Point25D ) );
3121 mGeom2 = createEmptyGeometry( wkbType25d );
3122 }
3123 }
3124
3125 // drop m/z value if not present in output wkb type
3126 if ( !QgsWkbTypes::hasZ( mWkbType ) && QgsWkbTypes::hasZ( geom.wkbType() ) )
3127 geom.get()->dropZValue();
3128 if ( !QgsWkbTypes::hasM( mWkbType ) && QgsWkbTypes::hasM( geom.wkbType() ) )
3129 geom.get()->dropMValue();
3130
3131 // add m/z values if not present in the input wkb type -- this is needed for formats which determine
3132 // geometry type based on features, e.g. geojson
3133 if ( QgsWkbTypes::hasZ( mWkbType ) && !QgsWkbTypes::hasZ( geom.wkbType() ) )
3134 {
3135 if ( mOgrDriverName == QLatin1String( "ESRI Shapefile" ) )
3136 geom.get()->addZValue( std::numeric_limits<double>::quiet_NaN() );
3137 else
3138 geom.get()->addZValue( 0 );
3139 }
3140 if ( QgsWkbTypes::hasM( mWkbType ) && !QgsWkbTypes::hasM( geom.wkbType() ) )
3141 {
3142 if ( mOgrDriverName == QLatin1String( "ESRI Shapefile" ) )
3143 geom.get()->addMValue( std::numeric_limits<double>::quiet_NaN() );
3144 else
3145 geom.get()->addMValue( 0 );
3146 }
3147
3148 if ( !mGeom2 )
3149 {
3150 // there's a problem when layer type is set as wkbtype Polygon
3151 // although there are also features of type MultiPolygon
3152 // (at least in OGR provider)
3153 // If the feature's wkbtype is different from the layer's wkbtype,
3154 // try to export it too.
3155 //
3156 // Btw. OGRGeometry must be exactly of the type of the geometry which it will receive
3157 // i.e. Polygons can't be imported to OGRMultiPolygon
3158 mGeom2 = createEmptyGeometry( geom.wkbType() );
3159 }
3160
3161 if ( !mGeom2 )
3162 {
3163 mErrorMessage = QObject::tr( "Feature geometry not imported (OGR error: %1)" )
3164 .arg( QString::fromUtf8( CPLGetLastErrorMsg() ) );
3166 QgsMessageLog::logMessage( mErrorMessage, QObject::tr( "OGR" ) );
3167 return nullptr;
3168 }
3169
3171 if ( mOgrDriverName == QLatin1String( "ESRI Shapefile" ) )
3173
3174 QByteArray wkb( geom.asWkb( wkbFlags ) );
3175 OGRErr err = OGR_G_ImportFromWkb( mGeom2, reinterpret_cast<unsigned char *>( const_cast<char *>( wkb.constData() ) ), wkb.length() );
3176 if ( err != OGRERR_NONE )
3177 {
3178 mErrorMessage = QObject::tr( "Feature geometry not imported (OGR error: %1)" )
3179 .arg( QString::fromUtf8( CPLGetLastErrorMsg() ) );
3181 QgsMessageLog::logMessage( mErrorMessage, QObject::tr( "OGR" ) );
3182 return nullptr;
3183 }
3184
3185 // pass ownership to geometry
3186 OGR_F_SetGeometryDirectly( poFeature.get(), mGeom2 );
3187 }
3188 else // wkb type matches
3189 {
3191 if ( mOgrDriverName == QLatin1String( "ESRI Shapefile" ) )
3193
3194 QByteArray wkb( geom.asWkb( wkbFlags ) );
3195 OGRGeometryH ogrGeom = createEmptyGeometry( mWkbType );
3196 OGRErr err = OGR_G_ImportFromWkb( ogrGeom, reinterpret_cast<unsigned char *>( const_cast<char *>( wkb.constData() ) ), wkb.length() );
3197 if ( err != OGRERR_NONE )
3198 {
3199 mErrorMessage = QObject::tr( "Feature geometry not imported (OGR error: %1)" )
3200 .arg( QString::fromUtf8( CPLGetLastErrorMsg() ) );
3202 QgsMessageLog::logMessage( mErrorMessage, QObject::tr( "OGR" ) );
3203 return nullptr;
3204 }
3205
3206 // set geometry (ownership is passed to OGR)
3207 OGR_F_SetGeometryDirectly( poFeature.get(), ogrGeom );
3208 }
3209 }
3210 else
3211 {
3212 OGR_F_SetGeometryDirectly( poFeature.get(), createEmptyGeometry( mWkbType ) );
3213 }
3214 }
3215 return poFeature;
3216}
3217
3218void QgsVectorFileWriter::resetMap( const QgsAttributeList &attributes )
3219{
3220 QMap<int, int> omap( mAttrIdxToOgrIdx );
3221 mAttrIdxToOgrIdx.clear();
3222 for ( int i = 0; i < attributes.size(); i++ )
3223 {
3224 if ( omap.find( i ) != omap.end() )
3225 mAttrIdxToOgrIdx.insert( attributes[i], omap[i] );
3226 }
3227}
3228
3229bool QgsVectorFileWriter::writeFeature( OGRLayerH layer, OGRFeatureH feature )
3230{
3231 if ( OGR_L_CreateFeature( layer, feature ) != OGRERR_NONE )
3232 {
3233 mErrorMessage = QObject::tr( "Feature creation error (OGR error: %1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) );
3235 QgsMessageLog::logMessage( mErrorMessage, QObject::tr( "OGR" ) );
3236 return false;
3237 }
3238 return true;
3239}
3240
3242{
3243 if ( mUsingTransaction )
3244 {
3245 if ( OGRERR_NONE != OGR_L_CommitTransaction( mLayer ) )
3246 {
3247 QgsDebugError( QStringLiteral( "Error while committing transaction on OGRLayer." ) );
3248 }
3249 }
3250 mDS.reset();
3251
3252 if ( mOgrRef )
3253 {
3254 OSRRelease( mOgrRef );
3255 }
3256}
3257
3260 const QString &fileName,
3261 const QString &fileEncoding,
3262 const QgsCoordinateReferenceSystem &destCRS,
3263 const QString &driverName,
3264 bool onlySelected,
3265 QString *errorMessage,
3266 const QStringList &datasourceOptions,
3267 const QStringList &layerOptions,
3268 bool skipAttributeCreation,
3269 QString *newFilename,
3270 Qgis::FeatureSymbologyExport symbologyExport,
3271 double symbologyScale,
3272 const QgsRectangle *filterExtent,
3273 Qgis::WkbType overrideGeometryType,
3274 bool forceMulti,
3275 bool includeZ,
3276 const QgsAttributeList &attributes,
3277 FieldValueConverter *fieldValueConverter,
3278 QString *newLayer )
3279{
3281 if ( destCRS.isValid() && layer )
3282 {
3283 ct = QgsCoordinateTransform( layer->crs(), destCRS, layer->transformContext() );
3284 }
3285
3286 SaveVectorOptions options;
3287 options.fileEncoding = fileEncoding;
3288 options.ct = ct;
3289 options.driverName = driverName;
3290 options.onlySelectedFeatures = onlySelected;
3291 options.datasourceOptions = datasourceOptions;
3292 options.layerOptions = layerOptions;
3293 options.skipAttributeCreation = skipAttributeCreation;
3296 if ( filterExtent )
3297 options.filterExtent = *filterExtent;
3298 options.overrideGeometryType = overrideGeometryType;
3299 options.forceMulti = forceMulti;
3300 options.includeZ = includeZ;
3301 options.attributes = attributes;
3302 options.fieldValueConverter = fieldValueConverter;
3303 return writeAsVectorFormatV3( layer, fileName, layer->transformContext(), options, errorMessage, newFilename, newLayer );
3304}
3305
3307 const QString &fileName,
3308 const QString &fileEncoding,
3309 const QgsCoordinateTransform &ct,
3310 const QString &driverName,
3311 bool onlySelected,
3312 QString *errorMessage,
3313 const QStringList &datasourceOptions,
3314 const QStringList &layerOptions,
3315 bool skipAttributeCreation,
3316 QString *newFilename,
3317 Qgis::FeatureSymbologyExport symbologyExport,
3318 double symbologyScale,
3319 const QgsRectangle *filterExtent,
3320 Qgis::WkbType overrideGeometryType,
3321 bool forceMulti,
3322 bool includeZ,
3323 const QgsAttributeList &attributes,
3324 FieldValueConverter *fieldValueConverter,
3325 QString *newLayer )
3326{
3327 SaveVectorOptions options;
3328 options.fileEncoding = fileEncoding;
3329 options.ct = ct;
3330 options.driverName = driverName;
3331 options.onlySelectedFeatures = onlySelected;
3332 options.datasourceOptions = datasourceOptions;
3333 options.layerOptions = layerOptions;
3334 options.skipAttributeCreation = skipAttributeCreation;
3337 if ( filterExtent )
3338 options.filterExtent = *filterExtent;
3339 options.overrideGeometryType = overrideGeometryType;
3340 options.forceMulti = forceMulti;
3341 options.includeZ = includeZ;
3342 options.attributes = attributes;
3343 options.fieldValueConverter = fieldValueConverter;
3344 return writeAsVectorFormatV3( layer, fileName, layer->transformContext(), options, errorMessage, newFilename, newLayer );
3345}
3346
3347
3349 : driverName( QStringLiteral( "GPKG" ) )
3350{
3351}
3352
3353
3354
3355QgsVectorFileWriter::WriterError QgsVectorFileWriter::prepareWriteAsVectorFormat( QgsVectorLayer *layer, const QgsVectorFileWriter::SaveVectorOptions &options, QgsVectorFileWriter::PreparedWriterDetails &details )
3356{
3357 if ( !layer || !layer->isValid() )
3358 {
3359 return ErrInvalidLayer;
3360 }
3361
3362 if ( layer->renderer() )
3363 details.renderer.reset( layer->renderer()->clone() );
3364 details.sourceCrs = layer->crs();
3365 details.sourceWkbType = layer->wkbType();
3366 details.sourceFields = layer->fields();
3367 details.providerType = layer->providerType();
3368 details.featureCount = options.onlySelectedFeatures ? layer->selectedFeatureCount() : layer->featureCount();
3369 if ( layer->dataProvider() )
3370 details.dataSourceUri = layer->dataProvider()->dataSourceUri();
3371 details.storageType = layer->storageType();
3372 details.selectedFeatureIds = layer->selectedFeatureIds();
3373 details.providerUriParams = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
3374
3375 if ( details.storageType == QLatin1String( "ESRI Shapefile" ) )
3376 {
3378 if ( options.onlySelectedFeatures )
3379 {
3380 req.setFilterFids( details.selectedFeatureIds );
3381 }
3382 req.setNoAttributes();
3383 details.geometryTypeScanIterator = layer->getFeatures( req );
3384 }
3385
3386 details.expressionContext = QgsExpressionContext( QgsExpressionContextUtils::globalProjectLayerScopes( layer ) );
3387 details.renderContext.setExpressionContext( details.expressionContext );
3388 details.renderContext.setRendererScale( options.symbologyScale );
3389
3390 details.shallTransform = false;
3391 if ( options.ct.isValid() )
3392 {
3393 // This means we should transform
3394 details.outputCrs = options.ct.destinationCrs();
3395 details.shallTransform = true;
3396 }
3397 else
3398 {
3399 // This means we shouldn't transform, use source CRS as output (if defined)
3400 details.outputCrs = details.sourceCrs;
3401 }
3402
3403 details.destWkbType = details.sourceWkbType;
3405 {
3406 details.destWkbType = QgsWkbTypes::flatType( options.overrideGeometryType );
3407 if ( QgsWkbTypes::hasZ( options.overrideGeometryType ) || options.includeZ )
3408 details.destWkbType = QgsWkbTypes::addZ( details.destWkbType );
3409 }
3410 if ( options.forceMulti )
3411 {
3412 details.destWkbType = QgsWkbTypes::multiType( details.destWkbType );
3413 }
3414
3415 details.attributes = options.attributes;
3416 if ( options.skipAttributeCreation )
3417 details.attributes.clear();
3418 else if ( details.attributes.isEmpty() )
3419 {
3420 const QgsAttributeList allAttributes = details.sourceFields.allAttributesList();
3421 for ( int idx : allAttributes )
3422 {
3423 QgsField fld = details.sourceFields.at( idx );
3424 if ( details.providerType == QLatin1String( "oracle" ) && fld.typeName().contains( QLatin1String( "SDO_GEOMETRY" ) ) )
3425 continue;
3426 details.attributes.append( idx );
3427 }
3428 }
3429
3430 if ( !details.attributes.isEmpty() )
3431 {
3432 for ( int attrIdx : std::as_const( details.attributes ) )
3433 {
3434 if ( details.sourceFields.exists( attrIdx ) )
3435 {
3436 QgsField field = details.sourceFields.at( attrIdx );
3437 field.setName( options.attributesExportNames.value( attrIdx, field.name() ) );
3438 details.outputFields.append( field );
3439 }
3440 else
3441 {
3442 QgsDebugError( QStringLiteral( "No such source field with index '%1' available." ).arg( attrIdx ) );
3443 }
3444 }
3445 }
3446
3447 // not ideal - would be nice to avoid this happening in the preparation step if possible,
3448 // but currently requires access to the layer's minimumValue/maximumValue methods
3449 if ( details.providerType == QLatin1String( "spatialite" ) )
3450 {
3451 for ( int i = 0; i < details.outputFields.size(); i++ )
3452 {
3453 if ( details.outputFields.at( i ).type() == QMetaType::Type::LongLong )
3454 {
3455 QVariant min;
3456 QVariant max;
3457 layer->minimumAndMaximumValue( i, min, max );
3458 if ( std::max( std::llabs( min.toLongLong() ), std::llabs( max.toLongLong() ) ) < std::numeric_limits<int>::max() )
3459 {
3460 details.outputFields[i].setType( QMetaType::Type::Int );
3461 }
3462 }
3463 }
3464 }
3465
3466
3467 //add possible attributes needed by renderer
3468 addRendererAttributes( details.renderer.get(), details.renderContext, details.sourceFields, details.attributes );
3469
3471 req.setSubsetOfAttributes( details.attributes );
3472 if ( options.onlySelectedFeatures )
3473 req.setFilterFids( details.selectedFeatureIds );
3474
3475 if ( !options.filterExtent.isNull() )
3476 {
3477 QgsRectangle filterRect = options.filterExtent;
3478 bool useFilterRect = true;
3479 if ( details.shallTransform )
3480 {
3481 try
3482 {
3483 // map filter rect back from destination CRS to layer CRS
3484 QgsCoordinateTransform extentTransform = options.ct;
3485 extentTransform.setBallparkTransformsAreAppropriate( true );
3486 filterRect = extentTransform.transformBoundingBox( filterRect, Qgis::TransformDirection::Reverse );
3487 }
3488 catch ( QgsCsException & )
3489 {
3490 useFilterRect = false;
3491 }
3492 }
3493 if ( useFilterRect )
3494 {
3495 req.setFilterRect( filterRect );
3496 }
3497 details.filterRectGeometry = QgsGeometry::fromRect( options.filterExtent );
3498 details.filterRectEngine.reset( QgsGeometry::createGeometryEngine( details.filterRectGeometry.constGet() ) );
3499 details.filterRectEngine->prepareGeometry();
3500 }
3501 details.sourceFeatureIterator = layer->getFeatures( req );
3502
3503 if ( !options.sourceDatabaseProviderConnection )
3504 {
3505 details.sourceDatabaseProviderConnection.reset( QgsMapLayerUtils::databaseConnection( layer ) );
3506 }
3507
3508 return NoError;
3509}
3510
3511QgsVectorFileWriter::WriterError QgsVectorFileWriter::writeAsVectorFormat( PreparedWriterDetails &details, const QString &fileName, const QgsVectorFileWriter::SaveVectorOptions &options, QString *newFilename, QString *errorMessage, QString *newLayer )
3512{
3513 return writeAsVectorFormatV2( details, fileName, QgsCoordinateTransformContext(), options, newFilename, newLayer, errorMessage );
3514}
3515
3516QgsVectorFileWriter::WriterError QgsVectorFileWriter::writeAsVectorFormatV2( PreparedWriterDetails &details, const QString &fileName, const QgsCoordinateTransformContext &transformContext, const QgsVectorFileWriter::SaveVectorOptions &options, QString *newFilename, QString *newLayer, QString *errorMessage, SinkFlags sinkFlags )
3517{
3518 Qgis::WkbType destWkbType = details.destWkbType;
3519
3520 int lastProgressReport = 0;
3521 long long total = details.featureCount;
3522
3523 // Special rules for OGR layers
3524 if ( details.providerType == QLatin1String( "ogr" ) && !details.dataSourceUri.isEmpty() )
3525 {
3526 QString srcFileName( details.providerUriParams.value( QStringLiteral( "path" ) ).toString() );
3527 if ( QFile::exists( srcFileName ) && QFileInfo( fileName ).canonicalFilePath() == QFileInfo( srcFileName ).canonicalFilePath() )
3528 {
3529 // Check the layer name too if it's a GPKG/SpatiaLite/SQLite OGR driver (pay attention: camel case in layerName)
3530 QgsDataSourceUri uri( details.dataSourceUri );
3531 if ( !( ( options.driverName == QLatin1String( "GPKG" ) ||
3532 options.driverName == QLatin1String( "SpatiaLite" ) ||
3533 options.driverName == QLatin1String( "SQLite" ) ) &&
3534 options.layerName != details.providerUriParams.value( QStringLiteral( "layerName" ) ) ) )
3535 {
3536 if ( errorMessage )
3537 *errorMessage = QObject::tr( "Cannot overwrite an OGR layer in place" );
3538 return ErrCreateDataSource;
3539 }
3540 }
3541
3542 // Shapefiles might contain multi types although wkbType() only reports singles
3543 if ( details.storageType == QLatin1String( "ESRI Shapefile" ) && !QgsWkbTypes::isMultiType( destWkbType ) )
3544 {
3545 QgsFeatureIterator fit = details.geometryTypeScanIterator;
3546 QgsFeature fet;
3547 long scanned = 0;
3548 while ( fit.nextFeature( fet ) )
3549 {
3550 if ( options.feedback && options.feedback->isCanceled() )
3551 {
3552 return Canceled;
3553 }
3554 if ( options.feedback )
3555 {
3556 //dedicate first 5% of progress bar to this scan
3557 int newProgress = static_cast<int>( ( 5.0 * scanned ) / total );
3558 if ( newProgress != lastProgressReport )
3559 {
3560 lastProgressReport = newProgress;
3561 options.feedback->setProgress( lastProgressReport );
3562 }
3563 }
3564
3565 if ( fet.hasGeometry() && QgsWkbTypes::isMultiType( fet.geometry().wkbType() ) )
3566 {
3567 destWkbType = QgsWkbTypes::multiType( destWkbType );
3568 break;
3569 }
3570 scanned++;
3571 }
3572 }
3573 }
3574
3575 QString tempNewFilename;
3576 QString tempNewLayer;
3577
3578 QgsVectorFileWriter::SaveVectorOptions newOptions = options;
3579 if ( !newOptions.sourceDatabaseProviderConnection )
3580 {
3581 newOptions.sourceDatabaseProviderConnection = details.sourceDatabaseProviderConnection.get();
3582 }
3583
3584 std::unique_ptr< QgsVectorFileWriter > writer( create( fileName, details.outputFields, destWkbType, details.outputCrs, transformContext, newOptions, sinkFlags, &tempNewFilename, &tempNewLayer ) );
3585 writer->setSymbologyScale( options.symbologyScale );
3586
3587 if ( newFilename )
3588 *newFilename = tempNewFilename;
3589
3590 if ( newLayer )
3591 *newLayer = tempNewLayer;
3592
3593 if ( newFilename )
3594 {
3595 QgsDebugMsgLevel( "newFilename = " + *newFilename, 2 );
3596 }
3597
3598 // check whether file creation was successful
3599 WriterError err = writer->hasError();
3600 if ( err != NoError )
3601 {
3602 if ( errorMessage )
3603 *errorMessage = writer->errorMessage();
3604 return err;
3605 }
3606
3607 if ( errorMessage )
3608 {
3609 errorMessage->clear();
3610 }
3611
3612 QgsFeature fet;
3613
3614 //create symbol table if needed
3615 if ( writer->symbologyExport() != Qgis::FeatureSymbologyExport::NoSymbology )
3616 {
3617 //writer->createSymbolLayerTable( layer, writer->mDS );
3618 }
3619
3620 switch ( writer->symbologyExport() )
3621 {
3623 {
3624 QgsFeatureRenderer *r = details.renderer.get();
3626 && r->usingSymbolLevels() )
3627 {
3628 QgsVectorFileWriter::WriterError error = writer->exportFeaturesSymbolLevels( details, details.sourceFeatureIterator, options.ct, errorMessage );
3629 return ( error == NoError ) ? NoError : ErrFeatureWriteFailed;
3630 }
3631 break;
3632 }
3635 break;
3636 }
3637
3638 int n = 0, errors = 0;
3639
3640 //unit type
3641 Qgis::DistanceUnit mapUnits = details.sourceCrs.mapUnits();
3642 if ( options.ct.isValid() )
3643 {
3644 mapUnits = options.ct.destinationCrs().mapUnits();
3645 }
3646
3647 writer->startRender( details.renderer.get(), details.sourceFields );
3648
3649 writer->resetMap( details.attributes );
3650 // Reset mFields to layer fields, and not just exported fields
3651 writer->mFields = details.sourceFields;
3652
3653 // write all features
3654 long saved = 0;
3655 int initialProgress = lastProgressReport;
3656 while ( details.sourceFeatureIterator.nextFeature( fet ) )
3657 {
3658 if ( options.feedback && options.feedback->isCanceled() )
3659 {
3660 return Canceled;
3661 }
3662
3663 saved++;
3664 if ( options.feedback )
3665 {
3666 //avoid spamming progress reports
3667 int newProgress = static_cast<int>( initialProgress + ( ( 100.0 - initialProgress ) * saved ) / total );
3668 if ( newProgress < 100 && newProgress != lastProgressReport )
3669 {
3670 lastProgressReport = newProgress;
3671 options.feedback->setProgress( lastProgressReport );
3672 }
3673 }
3674
3675 if ( details.shallTransform )
3676 {
3677 try
3678 {
3679 if ( fet.hasGeometry() )
3680 {
3681 QgsGeometry g = fet.geometry();
3682 g.transform( options.ct );
3683 fet.setGeometry( g );
3684 }
3685 }
3686 catch ( QgsCsException &e )
3687 {
3688 const QString msg = QObject::tr( "Failed to transform feature with ID '%1'. Writing stopped. (Exception: %2)" )
3689 .arg( fet.id() ).arg( e.what() );
3690 QgsLogger::warning( msg );
3691 if ( errorMessage )
3692 *errorMessage = msg;
3693
3694 return ErrProjection;
3695 }
3696 }
3697
3698 if ( fet.hasGeometry() && details.filterRectEngine && !details.filterRectEngine->intersects( fet.geometry().constGet() ) )
3699 continue;
3700
3701 if ( details.attributes.empty() && options.skipAttributeCreation )
3702 {
3703 fet.initAttributes( 0 );
3704 }
3705
3706 if ( !writer->addFeatureWithStyle( fet, writer->mRenderer.get(), mapUnits ) )
3707 {
3708 WriterError err = writer->hasError();
3709 if ( err != NoError && errorMessage )
3710 {
3711 if ( errorMessage->isEmpty() )
3712 {
3713 *errorMessage = QObject::tr( "Feature write errors:" );
3714 }
3715 *errorMessage += '\n' + writer->errorMessage();
3716 }
3717 errors++;
3718
3719 if ( errors > 1000 )
3720 {
3721 if ( errorMessage )
3722 {
3723 *errorMessage += QObject::tr( "Stopping after %n error(s)", nullptr, errors );
3724 }
3725
3726 n = -1;
3727 break;
3728 }
3729 }
3730 n++;
3731 }
3732
3733 writer->stopRender();
3734
3735 if ( errors > 0 && errorMessage && n > 0 )
3736 {
3737 *errorMessage += QObject::tr( "\nOnly %1 of %2 features written." ).arg( n - errors ).arg( n );
3738 }
3739
3740 writer.reset();
3741
3742 bool metadataFailure = false;
3743 if ( options.saveMetadata )
3744 {
3745 QString uri = QgsProviderRegistry::instance()->encodeUri( QStringLiteral( "ogr" ), QVariantMap
3746 {
3747 {QStringLiteral( "path" ), tempNewFilename },
3748 {QStringLiteral( "layerName" ), tempNewLayer }
3749 } );
3750
3751 try
3752 {
3753 QString error;
3754 if ( !QgsProviderRegistry::instance()->saveLayerMetadata( QStringLiteral( "ogr" ), uri, options.layerMetadata, error ) )
3755 {
3756 if ( errorMessage )
3757 {
3758 if ( !errorMessage->isEmpty() )
3759 *errorMessage += '\n';
3760 *errorMessage += error;
3761 }
3762 metadataFailure = true;
3763 }
3764 }
3765 catch ( QgsNotSupportedException &e )
3766 {
3767 if ( errorMessage )
3768 {
3769 if ( !errorMessage->isEmpty() )
3770 *errorMessage += '\n';
3771 *errorMessage += e.what();
3772 }
3773 metadataFailure = true;
3774 }
3775 }
3776
3777 return errors == 0 ? ( !metadataFailure ? NoError : ErrSavingMetadata ) : ErrFeatureWriteFailed;
3778}
3779
3781 const QString &fileName,
3782 const SaveVectorOptions &options,
3783 QString *newFilename,
3784 QString *errorMessage,
3785 QString *newLayer )
3786{
3787 QgsVectorFileWriter::PreparedWriterDetails details;
3788 WriterError err = prepareWriteAsVectorFormat( layer, options, details );
3789 if ( err != NoError )
3790 return err;
3791
3792 return writeAsVectorFormatV2( details, fileName, layer->transformContext(), options, newFilename, newLayer, errorMessage );
3793}
3794
3796 const QString &fileName,
3797 const QgsCoordinateTransformContext &transformContext,
3798 const SaveVectorOptions &options,
3799 QString *newFilename,
3800 QString *newLayer,
3801 QString *errorMessage )
3802{
3803 QgsVectorFileWriter::PreparedWriterDetails details;
3804 WriterError err = prepareWriteAsVectorFormat( layer, options, details );
3805 if ( err != NoError )
3806 return err;
3807
3808 return writeAsVectorFormatV2( details, fileName, transformContext, options, newFilename, newLayer, errorMessage );
3809}
3810
3811QgsVectorFileWriter::WriterError QgsVectorFileWriter::writeAsVectorFormatV3( QgsVectorLayer *layer, const QString &fileName, const QgsCoordinateTransformContext &transformContext, const QgsVectorFileWriter::SaveVectorOptions &options, QString *errorMessage, QString *newFilename, QString *newLayer )
3812{
3813 QgsVectorFileWriter::PreparedWriterDetails details;
3814 WriterError err = prepareWriteAsVectorFormat( layer, options, details );
3815 if ( err != NoError )
3816 return err;
3817
3818 return writeAsVectorFormatV2( details, fileName, transformContext, options, newFilename, newLayer, errorMessage );
3819}
3820
3821bool QgsVectorFileWriter::deleteShapeFile( const QString &fileName )
3822{
3823 QFileInfo fi( fileName );
3824 QDir dir = fi.dir();
3825
3826 QStringList filter;
3827 for ( const char *suffix : { ".shp", ".shx", ".dbf", ".prj", ".qix", ".qpj", ".cpg", ".sbn", ".sbx", ".idm", ".ind" } )
3828 {
3829 filter << fi.completeBaseName() + suffix;
3830 }
3831
3832 bool ok = true;
3833 const auto constEntryList = dir.entryList( filter );
3834 for ( const QString &file : constEntryList )
3835 {
3836 QFile f( dir.canonicalPath() + '/' + file );
3837 if ( !f.remove() )
3838 {
3839 QgsDebugError( QStringLiteral( "Removing file %1 failed: %2" ).arg( file, f.errorString() ) );
3840 ok = false;
3841 }
3842 }
3843
3844 return ok;
3845}
3846
3848{
3849 mSymbologyScale = d;
3850 mRenderContext.setRendererScale( mSymbologyScale );
3851}
3852
3854{
3855 QStringList driverNames;
3856 const QSet< QString > multiLayerExtensions = qgis::listToSet( QgsGdalUtils::multiLayerFileExtensions() );
3857
3858 for ( int i = 0; i < GDALGetDriverCount(); ++i )
3859 {
3860 GDALDriverH driver = GDALGetDriver( i );
3861 if ( !driver )
3862 {
3863 QgsLogger::warning( "unable to get driver " + QString::number( i ) );
3864 continue;
3865 }
3866
3867 const QString driverExtensions = GDALGetMetadataItem( driver, GDAL_DMD_EXTENSIONS, "" );
3868 if ( driverExtensions.isEmpty() )
3869 continue;
3870
3871 const QSet< QString > splitExtensions = qgis::listToSet( driverExtensions.split( ' ', Qt::SkipEmptyParts ) );
3872 if ( splitExtensions.intersects( multiLayerExtensions ) )
3873 {
3874 driverNames << GDALGetDescription( driver );
3875 }
3876 }
3877 return driverNames;
3878}
3879
3880QList< QgsVectorFileWriter::FilterFormatDetails > QgsVectorFileWriter::supportedFiltersAndFormats( const VectorFormatOptions options )
3881{
3882 static QReadWriteLock sFilterLock;
3883 static QMap< VectorFormatOptions, QList< QgsVectorFileWriter::FilterFormatDetails > > sFilters;
3884
3885 QgsReadWriteLocker locker( sFilterLock, QgsReadWriteLocker::Read );
3886
3887 const auto it = sFilters.constFind( options );
3888 if ( it != sFilters.constEnd() )
3889 return it.value();
3890
3892 QList< QgsVectorFileWriter::FilterFormatDetails > results;
3893
3895 int const drvCount = OGRGetDriverCount();
3896
3897 const QStringList multiLayerDrivers = multiLayerFormats();
3898
3899 for ( int i = 0; i < drvCount; ++i )
3900 {
3901 OGRSFDriverH drv = OGRGetDriver( i );
3902 if ( drv )
3903 {
3904 const QString drvName = GDALGetDescription( drv );
3905
3906 if ( options & SupportsMultipleLayers )
3907 {
3908 if ( !multiLayerDrivers.contains( drvName ) )
3909 continue;
3910 }
3911
3912 GDALDriverH gdalDriver = GDALGetDriverByName( drvName.toLocal8Bit().constData() );
3913 bool nonSpatialFormat = false;
3914 if ( gdalDriver )
3915 {
3916 nonSpatialFormat = GDALGetMetadataItem( gdalDriver, GDAL_DCAP_NONSPATIAL, nullptr );
3917 }
3918
3919 if ( OGR_Dr_TestCapability( drv, "CreateDataSource" ) != 0 )
3920 {
3921 if ( options & SkipNonSpatialFormats )
3922 {
3923 // skip non-spatial formats
3924 if ( nonSpatialFormat )
3925 continue;
3926 }
3927
3928 QString filterString = filterForDriver( drvName );
3929 if ( filterString.isEmpty() )
3930 continue;
3931
3932 MetaData metadata;
3933 QStringList globs;
3934 if ( driverMetadata( drvName, metadata ) && !metadata.glob.isEmpty() )
3935 {
3936 globs = metadata.glob.toLower().split( ' ' );
3937 }
3938
3939 FilterFormatDetails details;
3940 details.driverName = drvName;
3941 details.filterString = filterString;
3942 details.globs = globs;
3943
3944 results << details;
3945 }
3946 }
3947 }
3948
3949 std::sort( results.begin(), results.end(), [options]( const FilterFormatDetails & a, const FilterFormatDetails & b ) -> bool
3950 {
3951 if ( options & SortRecommended )
3952 {
3953 if ( a.driverName == QLatin1String( "GPKG" ) )
3954 return true; // Make https://twitter.com/shapefiIe a sad little fellow
3955 else if ( b.driverName == QLatin1String( "GPKG" ) )
3956 return false;
3957 else if ( a.driverName == QLatin1String( "ESRI Shapefile" ) )
3958 return true;
3959 else if ( b.driverName == QLatin1String( "ESRI Shapefile" ) )
3960 return false;
3961 }
3962
3963 return a.filterString.toLower().localeAwareCompare( b.filterString.toLower() ) < 0;
3964 } );
3965
3966 sFilters.insert( options, results );
3967 return results;
3968}
3969
3971{
3972 const auto formats = supportedFiltersAndFormats( options );
3973 QSet< QString > extensions;
3974
3975 const thread_local QRegularExpression rx( QStringLiteral( "\\*\\.(.*)$" ) );
3976
3977 for ( const FilterFormatDetails &format : formats )
3978 {
3979 for ( const QString &glob : format.globs )
3980 {
3981 const QRegularExpressionMatch match = rx.match( glob );
3982 if ( !match.hasMatch() )
3983 continue;
3984
3985 const QString matched = match.captured( 1 );
3986 extensions.insert( matched );
3987 }
3988 }
3989
3990 QStringList extensionList( extensions.constBegin(), extensions.constEnd() );
3991
3992 std::sort( extensionList.begin(), extensionList.end(), [options]( const QString & a, const QString & b ) -> bool
3993 {
3994 if ( options & SortRecommended )
3995 {
3996 if ( a == QLatin1String( "gpkg" ) )
3997 return true; // Make https://twitter.com/shapefiIe a sad little fellow
3998 else if ( b == QLatin1String( "gpkg" ) )
3999 return false;
4000 else if ( a == QLatin1String( "shp" ) )
4001 return true;
4002 else if ( b == QLatin1String( "shp" ) )
4003 return false;
4004 }
4005
4006 return a.toLower().localeAwareCompare( b.toLower() ) < 0;
4007 } );
4008
4009 return extensionList;
4010}
4011
4012QList< QgsVectorFileWriter::DriverDetails > QgsVectorFileWriter::ogrDriverList( const VectorFormatOptions options )
4013{
4014 QList< QgsVectorFileWriter::DriverDetails > results;
4015
4017 const int drvCount = OGRGetDriverCount();
4018
4019 const QStringList multiLayerDrivers = multiLayerFormats();
4020
4021 QStringList writableDrivers;
4022 for ( int i = 0; i < drvCount; ++i )
4023 {
4024 OGRSFDriverH drv = OGRGetDriver( i );
4025 if ( drv )
4026 {
4027 const QString drvName = GDALGetDescription( drv );
4028
4029 if ( options & SupportsMultipleLayers )
4030 {
4031 if ( !multiLayerDrivers.contains( drvName ) )
4032 continue;
4033 }
4034
4035 if ( options & SkipNonSpatialFormats )
4036 {
4037 // skip non-spatial formats
4038 // TODO - use GDAL metadata to determine this, when support exists in GDAL
4039 if ( drvName == QLatin1String( "ODS" ) || drvName == QLatin1String( "XLSX" ) || drvName == QLatin1String( "XLS" ) )
4040 continue;
4041 }
4042
4043 if ( drvName == QLatin1String( "ESRI Shapefile" ) )
4044 {
4045 writableDrivers << QStringLiteral( "DBF file" );
4046 }
4047 if ( OGR_Dr_TestCapability( drv, "CreateDataSource" ) != 0 )
4048 {
4049 // Add separate format for Mapinfo MIF (MITAB is OGR default)
4050 if ( drvName == QLatin1String( "MapInfo File" ) )
4051 {
4052 writableDrivers << QStringLiteral( "MapInfo MIF" );
4053 }
4054 else if ( drvName == QLatin1String( "SQLite" ) )
4055 {
4056 // Unfortunately it seems that there is no simple way to detect if
4057 // OGR SQLite driver is compiled with SpatiaLite support.
4058 // We have HAVE_SPATIALITE in QGIS, but that may differ from OGR
4059 // http://lists.osgeo.org/pipermail/gdal-dev/2012-November/034580.html
4060 // -> test if creation fails
4061 QString option = QStringLiteral( "SPATIALITE=YES" );
4062 char *options[2] = { CPLStrdup( option.toLocal8Bit().constData() ), nullptr };
4063 OGRSFDriverH poDriver;
4065 poDriver = OGRGetDriverByName( drvName.toLocal8Bit().constData() );
4066 if ( poDriver )
4067 {
4068 gdal::ogr_datasource_unique_ptr ds( OGR_Dr_CreateDataSource( poDriver, QStringLiteral( "/vsimem/spatialitetest.sqlite" ).toUtf8().constData(), options ) );
4069 if ( ds )
4070 {
4071 writableDrivers << QStringLiteral( "SpatiaLite" );
4072 OGR_Dr_DeleteDataSource( poDriver, QStringLiteral( "/vsimem/spatialitetest.sqlite" ).toUtf8().constData() );
4073 }
4074 }
4075 CPLFree( options[0] );
4076 }
4077 writableDrivers << drvName;
4078 }
4079 }
4080 }
4081
4082 results.reserve( writableDrivers.count() );
4083 for ( const QString &drvName : std::as_const( writableDrivers ) )
4084 {
4085 MetaData metadata;
4086 if ( driverMetadata( drvName, metadata ) && !metadata.trLongName.isEmpty() )
4087 {
4088 DriverDetails details;
4089 details.driverName = drvName;
4090 details.longName = metadata.trLongName;
4091 results << details;
4092 }
4093 }
4094
4095 std::sort( results.begin(), results.end(), [options]( const DriverDetails & a, const DriverDetails & b ) -> bool
4096 {
4097 if ( options & SortRecommended )
4098 {
4099 if ( a.driverName == QLatin1String( "GPKG" ) )
4100 return true; // Make https://twitter.com/shapefiIe a sad little fellow
4101 else if ( b.driverName == QLatin1String( "GPKG" ) )
4102 return false;
4103 else if ( a.driverName == QLatin1String( "ESRI Shapefile" ) )
4104 return true;
4105 else if ( b.driverName == QLatin1String( "ESRI Shapefile" ) )
4106 return false;
4107 }
4108
4109 return a.longName.toLower().localeAwareCompare( b.longName.toLower() ) < 0;
4110 } );
4111 return results;
4112}
4113
4114QString QgsVectorFileWriter::driverForExtension( const QString &extension )
4115{
4116 QString ext = extension.trimmed();
4117 if ( ext.isEmpty() )
4118 return QString();
4119
4120 if ( ext.startsWith( '.' ) )
4121 ext.remove( 0, 1 );
4122
4123 GDALAllRegister();
4124 int const drvCount = GDALGetDriverCount();
4125
4126 for ( int i = 0; i < drvCount; ++i )
4127 {
4128 GDALDriverH drv = GDALGetDriver( i );
4129 if ( drv )
4130 {
4131 char **driverMetadata = GDALGetMetadata( drv, nullptr );
4132 if ( CSLFetchBoolean( driverMetadata, GDAL_DCAP_CREATE, false ) && CSLFetchBoolean( driverMetadata, GDAL_DCAP_VECTOR, false ) )
4133 {
4134 QString drvName = GDALGetDriverShortName( drv );
4135 QStringList driverExtensions = QString( GDALGetMetadataItem( drv, GDAL_DMD_EXTENSIONS, nullptr ) ).split( ' ' );
4136
4137 const auto constDriverExtensions = driverExtensions;
4138 for ( const QString &driver : constDriverExtensions )
4139 {
4140 if ( driver.compare( ext, Qt::CaseInsensitive ) == 0 )
4141 return drvName;
4142 }
4143 }
4144 }
4145 }
4146 return QString();
4147}
4148
4150{
4151 QString filterString;
4152 const auto driverFormats = supportedFiltersAndFormats( options );
4153 for ( const FilterFormatDetails &details : driverFormats )
4154 {
4155 if ( !filterString.isEmpty() )
4156 filterString += QLatin1String( ";;" );
4157
4158 filterString += details.filterString;
4159 }
4160 return filterString;
4161}
4162
4163QString QgsVectorFileWriter::filterForDriver( const QString &driverName )
4164{
4165 MetaData metadata;
4166 if ( !driverMetadata( driverName, metadata ) || metadata.trLongName.isEmpty() || metadata.glob.isEmpty() )
4167 return QString();
4168
4169 return QStringLiteral( "%1 (%2 %3)" ).arg( metadata.trLongName,
4170 metadata.glob.toLower(),
4171 metadata.glob.toUpper() );
4172}
4173
4175{
4176 if ( codecName == QLatin1String( "System" ) )
4177 return QStringLiteral( "LDID/0" );
4178
4179 const thread_local QRegularExpression re( QRegularExpression::anchoredPattern( QString( "(CP|windows-|ISO[ -])(.+)" ) ), QRegularExpression::CaseInsensitiveOption );
4180 const QRegularExpressionMatch match = re.match( codecName );
4181 if ( match.hasMatch() )
4182 {
4183 QString c = match.captured( 2 ).remove( '-' );
4184 bool isNumber;
4185 ( void ) c.toInt( &isNumber );
4186 if ( isNumber )
4187 return c;
4188 }
4189 return codecName;
4190}
4191
4192void QgsVectorFileWriter::createSymbolLayerTable( QgsVectorLayer *vl, const QgsCoordinateTransform &ct, OGRDataSourceH ds )
4193{
4194 if ( !vl || !ds )
4195 {
4196 return;
4197 }
4198
4199 QgsFeatureRenderer *renderer = vl->renderer();
4200 if ( !renderer )
4201 {
4202 return;
4203 }
4204
4205 //unit type
4206 Qgis::DistanceUnit mapUnits = vl->crs().mapUnits();
4207 if ( ct.isValid() )
4208 {
4209 mapUnits = ct.destinationCrs().mapUnits();
4210 }
4211
4212 mSymbolLayerTable.clear();
4213 OGRStyleTableH ogrStyleTable = OGR_STBL_Create();
4214 OGRStyleMgrH styleManager = OGR_SM_Create( ogrStyleTable );
4215
4216 //get symbols
4217 int nTotalLevels = 0;
4218 QgsSymbolList symbolList = renderer->symbols( mRenderContext );
4219 QgsSymbolList::iterator symbolIt = symbolList.begin();
4220 for ( ; symbolIt != symbolList.end(); ++symbolIt )
4221 {
4222 double mmsf = mmScaleFactor( mSymbologyScale, ( *symbolIt )->outputUnit(), mapUnits );
4223 double musf = mapUnitScaleFactor( mSymbologyScale, ( *symbolIt )->outputUnit(), mapUnits );
4224
4225 int nLevels = ( *symbolIt )->symbolLayerCount();
4226 for ( int i = 0; i < nLevels; ++i )
4227 {
4228 mSymbolLayerTable.insert( ( *symbolIt )->symbolLayer( i ), QString::number( nTotalLevels ) );
4229 OGR_SM_AddStyle( styleManager, QString::number( nTotalLevels ).toLocal8Bit(),
4230 ( *symbolIt )->symbolLayer( i )->ogrFeatureStyle( mmsf, musf ).toLocal8Bit() );
4231 ++nTotalLevels;
4232 }
4233 }
4234 OGR_DS_SetStyleTableDirectly( ds, ogrStyleTable );
4235}
4236
4237QgsVectorFileWriter::WriterError QgsVectorFileWriter::exportFeaturesSymbolLevels( const PreparedWriterDetails &details, QgsFeatureIterator &fit,
4238 const QgsCoordinateTransform &ct, QString *errorMessage )
4239{
4240 if ( !details.renderer )
4241 return ErrInvalidLayer;
4242
4243 mRenderContext.expressionContext() = details.expressionContext;
4244
4245 QHash< QgsSymbol *, QList<QgsFeature> > features;
4246
4247 //unit type
4248 Qgis::DistanceUnit mapUnits = details.sourceCrs.mapUnits();
4249 if ( ct.isValid() )
4250 {
4251 mapUnits = ct.destinationCrs().mapUnits();
4252 }
4253
4254 startRender( details.renderer.get(), details.sourceFields );
4255
4256 //fetch features
4257 QgsFeature fet;
4258 QgsSymbol *featureSymbol = nullptr;
4259 while ( fit.nextFeature( fet ) )
4260 {
4261 if ( ct.isValid() )
4262 {
4263 try
4264 {
4265 if ( fet.hasGeometry() )
4266 {
4267 QgsGeometry g = fet.geometry();
4268 g.transform( ct );
4269 fet.setGeometry( g );
4270 }
4271 }
4272 catch ( QgsCsException &e )
4273 {
4274 QString msg = QObject::tr( "Failed to transform, writing stopped. (Exception: %1)" )
4275 .arg( e.what() );
4276 QgsLogger::warning( msg );
4277 if ( errorMessage )
4278 *errorMessage = msg;
4279
4280 return ErrProjection;
4281 }
4282 }
4283 mRenderContext.expressionContext().setFeature( fet );
4284
4285 featureSymbol = mRenderer->symbolForFeature( fet, mRenderContext );
4286 if ( !featureSymbol )
4287 {
4288 continue;
4289 }
4290
4291 QHash< QgsSymbol *, QList<QgsFeature> >::iterator it = features.find( featureSymbol );
4292 if ( it == features.end() )
4293 {
4294 it = features.insert( featureSymbol, QList<QgsFeature>() );
4295 }
4296 it.value().append( fet );
4297 }
4298
4299 //find out order
4300 QgsSymbolLevelOrder levels;
4301 QgsSymbolList symbols = mRenderer->symbols( mRenderContext );
4302 for ( int i = 0; i < symbols.count(); i++ )
4303 {
4304 QgsSymbol *sym = symbols[i];
4305 for ( int j = 0; j < sym->symbolLayerCount(); j++ )
4306 {
4307 int level = sym->symbolLayer( j )->renderingPass();
4308 if ( level < 0 || level >= 1000 ) // ignore invalid levels
4309 continue;
4310 QgsSymbolLevelItem item( sym, j );
4311 while ( level >= levels.count() ) // append new empty levels
4312 levels.append( QgsSymbolLevel() );
4313 levels[level].append( item );
4314 }
4315 }
4316
4317 int nErrors = 0;
4318 int nTotalFeatures = 0;
4319
4320 //export symbol layers and symbology
4321 for ( int l = 0; l < levels.count(); l++ )
4322 {
4323 QgsSymbolLevel &level = levels[l];
4324 for ( int i = 0; i < level.count(); i++ )
4325 {
4326 QgsSymbolLevelItem &item = level[i];
4327 QHash< QgsSymbol *, QList<QgsFeature> >::iterator levelIt = features.find( item.symbol() );
4328 if ( levelIt == features.end() )
4329 {
4330 ++nErrors;
4331 continue;
4332 }
4333
4334 double mmsf = mmScaleFactor( mSymbologyScale, levelIt.key()->outputUnit(), mapUnits );
4335 double musf = mapUnitScaleFactor( mSymbologyScale, levelIt.key()->outputUnit(), mapUnits );
4336
4337 int llayer = item.layer();
4338 QList<QgsFeature> &featureList = levelIt.value();
4339 QList<QgsFeature>::iterator featureIt = featureList.begin();
4340 for ( ; featureIt != featureList.end(); ++featureIt )
4341 {
4342 ++nTotalFeatures;
4343 gdal::ogr_feature_unique_ptr ogrFeature = createFeature( *featureIt );
4344 if ( !ogrFeature )
4345 {
4346 ++nErrors;
4347 continue;
4348 }
4349
4350 QString styleString = levelIt.key()->symbolLayer( llayer )->ogrFeatureStyle( mmsf, musf );
4351 if ( !styleString.isEmpty() )
4352 {
4353 OGR_F_SetStyleString( ogrFeature.get(), styleString.toLocal8Bit().constData() );
4354 if ( !writeFeature( mLayer, ogrFeature.get() ) )
4355 {
4356 ++nErrors;
4357 }
4358 }
4359 }
4360 }
4361 }
4362
4363 stopRender();
4364
4365 if ( nErrors > 0 && errorMessage )
4366 {
4367 *errorMessage += QObject::tr( "\nOnly %1 of %2 features written." ).arg( nTotalFeatures - nErrors ).arg( nTotalFeatures );
4368 }
4369
4371}
4372
4373double QgsVectorFileWriter::mmScaleFactor( double scale, Qgis::RenderUnit symbolUnits, Qgis::DistanceUnit mapUnits )
4374{
4375 if ( symbolUnits == Qgis::RenderUnit::Millimeters )
4376 {
4377 return 1.0;
4378 }
4379 else
4380 {
4381 //conversion factor map units -> mm
4382 if ( mapUnits == Qgis::DistanceUnit::Meters )
4383 {
4384 return 1000 / scale;
4385 }
4386
4387 }
4388 return 1.0; //todo: map units
4389}
4390
4391double QgsVectorFileWriter::mapUnitScaleFactor( double scale, Qgis::RenderUnit symbolUnits, Qgis::DistanceUnit mapUnits )
4392{
4393 if ( symbolUnits == Qgis::RenderUnit::MapUnits )
4394 {
4395 return 1.0;
4396 }
4397 else
4398 {
4399 if ( symbolUnits == Qgis::RenderUnit::Millimeters && mapUnits == Qgis::DistanceUnit::Meters )
4400 {
4401 return scale / 1000;
4402 }
4403 }
4404 return 1.0;
4405}
4406
4407void QgsVectorFileWriter::startRender( QgsFeatureRenderer *sourceRenderer, const QgsFields &fields )
4408{
4409 mRenderer = createSymbologyRenderer( sourceRenderer );
4410 if ( !mRenderer )
4411 {
4412 return;
4413 }
4414
4415 mRenderer->startRender( mRenderContext, fields );
4416}
4417
4418void QgsVectorFileWriter::stopRender()
4419{
4420 if ( !mRenderer )
4421 {
4422 return;
4423 }
4424
4425 mRenderer->stopRender( mRenderContext );
4426}
4427
4428std::unique_ptr<QgsFeatureRenderer> QgsVectorFileWriter::createSymbologyRenderer( QgsFeatureRenderer *sourceRenderer ) const
4429{
4430 switch ( mSymbologyExport )
4431 {
4433 {
4434 return nullptr;
4435 }
4438 break;
4439 }
4440
4441 if ( !sourceRenderer )
4442 {
4443 return nullptr;
4444 }
4445
4446 return std::unique_ptr< QgsFeatureRenderer >( sourceRenderer->clone() );
4447}
4448
4449void QgsVectorFileWriter::addRendererAttributes( QgsFeatureRenderer *renderer, QgsRenderContext &context, const QgsFields &fields, QgsAttributeList &attList )
4450{
4451 if ( renderer )
4452 {
4453 const QSet<QString> rendererAttributes = renderer->usedAttributes( context );
4454 for ( const QString &attr : rendererAttributes )
4455 {
4456 int index = fields.lookupField( attr );
4457 if ( index != -1 )
4458 {
4459 attList.append( index );
4460 }
4461 }
4462 }
4463}
4464
4465QStringList QgsVectorFileWriter::concatenateOptions( const QMap<QString, QgsVectorFileWriter::Option *> &options )
4466{
4467 QStringList list;
4468 QMap<QString, QgsVectorFileWriter::Option *>::ConstIterator it;
4469
4470 for ( it = options.constBegin(); it != options.constEnd(); ++it )
4471 {
4472 QgsVectorFileWriter::Option *option = it.value();
4473 switch ( option->type )
4474 {
4476 {
4477 QgsVectorFileWriter::IntOption *opt = dynamic_cast<QgsVectorFileWriter::IntOption *>( option );
4478 if ( opt )
4479 {
4480 list.append( QStringLiteral( "%1=%2" ).arg( it.key() ).arg( opt->defaultValue ) );
4481 }
4482 break;
4483 }
4484
4486 {
4487 QgsVectorFileWriter::SetOption *opt = dynamic_cast<QgsVectorFileWriter::SetOption *>( option );
4488 if ( opt && !opt->defaultValue.isEmpty() )
4489 {
4490 list.append( QStringLiteral( "%1=%2" ).arg( it.key(), opt->defaultValue ) );
4491 }
4492 break;
4493 }
4494
4496 {
4498 if ( opt && !opt->defaultValue.isNull() )
4499 {
4500 list.append( QStringLiteral( "%1=%2" ).arg( it.key(), opt->defaultValue ) );
4501 }
4502 break;
4503 }
4504
4507 if ( opt && !opt->mValue.isEmpty() )
4508 {
4509 list.append( QStringLiteral( "%1=%2" ).arg( it.key(), opt->mValue ) );
4510 }
4511 break;
4512 }
4513 }
4514
4515 return list;
4516}
4517
4519{
4520 OGRSFDriverH hDriver = nullptr;
4521 gdal::ogr_datasource_unique_ptr hDS( OGROpen( datasetName.toUtf8().constData(), TRUE, &hDriver ) );
4522 if ( !hDS )
4524 const QString drvName = GDALGetDescription( hDriver );
4526 if ( OGR_DS_TestCapability( hDS.get(), ODsCCreateLayer ) )
4527 {
4528 // Shapefile driver returns True for a "foo.shp" dataset name,
4529 // creating "bar.shp" new layer, but this would be a bit confusing
4530 // for the user, so pretent that it does not support that
4531 if ( !( drvName == QLatin1String( "ESRI Shapefile" ) && QFile::exists( datasetName ) ) )
4532 caps |= CanAddNewLayer;
4533 }
4534 if ( OGR_DS_TestCapability( hDS.get(), ODsCDeleteLayer ) )
4535 {
4536 caps |= CanDeleteLayer;
4537 }
4538 int layer_count = OGR_DS_GetLayerCount( hDS.get() );
4539 if ( layer_count )
4540 {
4541 OGRLayerH hLayer = OGR_DS_GetLayer( hDS.get(), 0 );
4542 if ( hLayer )
4543 {
4544 if ( OGR_L_TestCapability( hLayer, OLCSequentialWrite ) )
4545 {
4547 if ( OGR_L_TestCapability( hLayer, OLCCreateField ) )
4548 {
4550 }
4551 }
4552 }
4553 }
4554 return caps;
4555}
4556
4557bool QgsVectorFileWriter::targetLayerExists( const QString &datasetName,
4558 const QString &layerNameIn )
4559{
4560 OGRSFDriverH hDriver = nullptr;
4561 gdal::ogr_datasource_unique_ptr hDS( OGROpen( datasetName.toUtf8().constData(), TRUE, &hDriver ) );
4562 if ( !hDS )
4563 return false;
4564
4565 QString layerName( layerNameIn );
4566 if ( layerName.isEmpty() )
4567 layerName = QFileInfo( datasetName ).baseName();
4568
4569 return OGR_DS_GetLayerByName( hDS.get(), layerName.toUtf8().constData() );
4570}
4571
4572
4573bool QgsVectorFileWriter::areThereNewFieldsToCreate( const QString &datasetName,
4574 const QString &layerName,
4575 QgsVectorLayer *layer,
4576 const QgsAttributeList &attributes )
4577{
4578 OGRSFDriverH hDriver = nullptr;
4579 gdal::ogr_datasource_unique_ptr hDS( OGROpen( datasetName.toUtf8().constData(), TRUE, &hDriver ) );
4580 if ( !hDS )
4581 return false;
4582 OGRLayerH hLayer = OGR_DS_GetLayerByName( hDS.get(), layerName.toUtf8().constData() );
4583 if ( !hLayer )
4584 {
4585 return false;
4586 }
4587 bool ret = false;
4588 OGRFeatureDefnH defn = OGR_L_GetLayerDefn( hLayer );
4589 const auto constAttributes = attributes;
4590 for ( int idx : constAttributes )
4591 {
4592 QgsField fld = layer->fields().at( idx );
4593 if ( OGR_FD_GetFieldIndex( defn, fld.name().toUtf8().constData() ) < 0 )
4594 {
4595 ret = true;
4596 break;
4597 }
4598 }
4599 return ret;
4600}
@ FieldComments
Writer can support field comments.
@ FieldAliases
Writer can support field aliases.
DistanceUnit
Units of distance.
Definition qgis.h:4844
QFlags< VectorFileWriterCapability > VectorFileWriterCapabilities
Capabilities supported by a QgsVectorFileWriter object.
Definition qgis.h:1047
RenderUnit
Rendering size units.
Definition qgis.h:5014
@ Millimeters
Millimeters.
@ MapUnits
Map units.
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:256
@ LineString
LineString.
@ MultiPolygon25D
MultiPolygon25D.
@ GeometryCollectionZ
GeometryCollectionZ.
@ NoGeometry
No geometry.
@ MultiLineString
MultiLineString.
@ Unknown
Unknown.
@ PointZ
PointZ.
@ MultiPolygonZ
MultiPolygonZ.
@ Point25D
Point25D.
FeatureSymbologyExport
Options for exporting features considering their symbology.
Definition qgis.h:5338
@ PerFeature
Keeps the number of features and export symbology per feature.
@ PerSymbolLayer
Exports one feature per symbol layer (considering symbol levels)
@ NoSymbology
Export only data.
@ Reverse
Reverse/inverse transform (from destination to source)
Provides common functionality for database based connections.
virtual QgsFieldDomain * fieldDomain(const QString &name) const
Returns the field domain with the specified name from the provider.
virtual bool addZValue(double zValue=0)=0
Adds a z-dimension to the geometry, initialized to a preset value.
virtual bool dropMValue()=0
Drops any measure values which exist in the geometry.
QFlags< WkbFlag > WkbFlags
virtual bool addMValue(double mValue=0)=0
Adds a measure to the geometry, initialized to a preset value.
virtual bool dropZValue()=0
Drops any z-dimensions which exist in the geometry.
@ FlagExportTrianglesAsPolygons
Triangles should be exported as polygon geometries.
@ FlagExportNanAsDoubleMin
Use -DOUBLE_MAX to represent NaN.
static void registerOgrDrivers()
Register OGR drivers ensuring this only happens once.
Represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
static Q_INVOKABLE QgsCoordinateReferenceSystem fromEpsgId(long epsg)
Creates a CRS from a given EPSG ID.
Contains information about the context in which a coordinate transform is executed.
Handles coordinate transforms between two coordinate systems.
void setBallparkTransformsAreAppropriate(bool appropriate)
Sets whether approximate "ballpark" results are appropriate for this coordinate transform.
QgsRectangle transformBoundingBox(const QgsRectangle &rectangle, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool handle180Crossover=false) const
Transforms a rectangle from the source CRS to the destination CRS.
bool isValid() const
Returns true if the coordinate transform is valid, ie both the source and destination CRS have been s...
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system, which the transform will transform coordinates t...
Custom exception class for Coordinate Reference System related exceptions.
virtual QString dataSourceUri(bool expandAuthConfig=false) const
Gets the data source specification.
Stores the component parts of a data source URI (e.g.
QString what() const
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
Abstract base class for all 2D vector feature renderers.
virtual QgsSymbolList symbolsForFeature(const QgsFeature &feature, QgsRenderContext &context) const
Returns list of symbols used for rendering the feature.
virtual QgsSymbolList symbols(QgsRenderContext &context) const
Returns list of symbols used by the renderer.
bool usingSymbolLevels() const
virtual QgsFeatureRenderer::Capabilities capabilities()
Returns details about internals of this renderer.
virtual QSet< QString > usedAttributes(const QgsRenderContext &context) const =0
Returns a list of attributes required by this renderer.
@ SymbolLevels
Rendering with symbol levels (i.e. implements symbols(), symbolForFeature())
virtual QgsFeatureRenderer * clone() const =0
Create a deep copy of this renderer.
Wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFilterFids(const QgsFeatureIds &fids)
Sets the feature IDs that should be fetched.
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
QgsFeatureRequest & setNoAttributes()
Set that no attributes will be fetched.
QgsFeatureRequest & setFilterRect(const QgsRectangle &rectangle)
Sets the rectangle from which features will be taken.
QFlags< SinkFlag > SinkFlags
@ RegeneratePrimaryKey
This flag indicates, that a primary key field cannot be guaranteed to be unique and the sink should i...
QFlags< Flag > Flags
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:58
void initAttributes(int fieldCount)
Initialize this feature with the given number of fields.
QgsFeatureId id
Definition qgsfeature.h:66
QgsGeometry geometry
Definition qgsfeature.h:69
bool hasGeometry() const
Returns true if the feature has an associated geometry.
bool isUnsetValue(int fieldIdx) const
Returns true if the attribute at the specified index is an unset value.
Q_INVOKABLE QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:53
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition qgsfeedback.h:61
QString domainName() const
Returns the associated field domain name, for providers which support field domains.
@ ConstraintNotNull
Field may not be null.
@ ConstraintUnique
Field must have a unique value.
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:53
QMetaType::Type type
Definition qgsfield.h:60
QString typeName() const
Gets the field type.
Definition qgsfield.cpp:162
QString name
Definition qgsfield.h:62
int precision
Definition qgsfield.h:59
int length
Definition qgsfield.h:58
bool convertCompatible(QVariant &v, QString *errorMessage=nullptr) const
Converts the provided variant to a compatible format.
Definition qgsfield.cpp:474
void setName(const QString &name)
Set the field name.
Definition qgsfield.cpp:228
QMetaType::Type subType() const
If the field is a collection, gets its element's type.
Definition qgsfield.cpp:157
QString alias
Definition qgsfield.h:63
QString comment
Definition qgsfield.h:61
QgsFieldConstraints constraints
Definition qgsfield.h:65
Container of fields for a vector layer.
Definition qgsfields.h:46
int count
Definition qgsfields.h:50
int size() const
Returns number of items.
void clear()
Removes all fields.
Definition qgsfields.cpp:58
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Q_INVOKABLE int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
static QStringList multiLayerFileExtensions()
Returns a list of file extensions which potentially contain multiple layers representing GDAL raster ...
A geometry is the spatial representation of a feature.
static QgsGeometry fromRect(const QgsRectangle &rect)
Creates a new geometry from a QgsRectangle.
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false)
Transforms this geometry as described by the coordinate transform ct.
QgsAbstractGeometry * get()
Returns a modifiable (non-const) reference to the underlying abstract geometry primitive.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
bool convertToMultiType()
Converts single type geometry into multitype geometry e.g.
QByteArray asWkb(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const
Export the geometry to WKB.
Qgis::WkbType wkbType() const
Returns type of the geometry as a WKB type (point / linestring / polygon etc.)
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry, double precision=0.0, Qgis::GeosCreationFlags flags=Qgis::GeosCreationFlag::SkipEmptyInteriorRings)
Creates and returns a new geometry engine representing the specified geometry using precision on a gr...
Sets the current locale to the c locale for the lifetime of the object.
Definition qgslocalec.h:32
static void warning(const QString &msg)
Goes to qWarning.
static QgsAbstractDatabaseProviderConnection * databaseConnection(const QgsMapLayer *layer)
Creates and returns the (possibly nullptr) database connection for a layer.
QString providerType() const
Returns the provider type (provider key) for this layer.
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:84
QgsCoordinateTransformContext transformContext() const
Returns the layer data provider coordinate transform context or a default transform context if the la...
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE())
Adds a message to the log instance (and creates it if necessary).
Custom exception class which is raised when an operation is not supported.
static OGRSpatialReferenceH crsToOGRSpatialReference(const QgsCoordinateReferenceSystem &crs)
Returns a OGRSpatialReferenceH corresponding to the specified crs object.
static std::unique_ptr< QgsFieldDomain > convertFieldDomain(OGRFieldDomainH domain)
Converts an OGR field domain definition to a QgsFieldDomain equivalent.
static int OGRTZFlagFromQt(const QDateTime &datetime)
Gets the value of OGRField::Date::TZFlag from the timezone of a QDateTime.
Custom exception class for provider connection related exceptions.
QVariantMap decodeUri(const QString &providerKey, const QString &uri)
Breaks a provider data source URI into its component paths (e.g.
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
QString encodeUri(const QString &providerKey, const QVariantMap &parts)
Reassembles a provider data source URI from its component paths (e.g.
bool saveLayerMetadata(const QString &providerKey, const QString &uri, const QgsLayerMetadata &metadata, QString &errorMessage)
Saves metadata to the layer corresponding to the specified uri.
A convenience class that simplifies locking and unlocking QReadWriteLocks.
@ Write
Lock for write.
void changeMode(Mode mode)
Change the mode of the lock to mode.
A rectangle specified with double values.
Contains information about the context of a rendering operation.
QgsExpressionContext & expressionContext()
Gets the expression context.
void setRendererScale(double scale)
Sets the renderer map scale.
Stores settings for use within QGIS.
Definition qgssettings.h:66
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
int renderingPass() const
Specifies the rendering pass in which this symbol layer should be rendered.
Represents a symbol level during vector rendering operations.
Definition qgsrenderer.h:64
int layer() const
The layer of this symbol level.
QgsSymbol * symbol() const
The symbol of this symbol level.
Abstract base class for all rendered symbols.
Definition qgssymbol.h:231
QgsSymbolLayer * symbolLayer(int layer)
Returns the symbol layer at the specified index.
int symbolLayerCount() const
Returns the total number of symbol layers contained in the symbol.
Definition qgssymbol.h:353
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
An available option for configuring file writing for a particular output format, presenting an boolea...
Interface to convert raw field values to their user-friendly values.
virtual QVariant convert(int fieldIdxInLayer, const QVariant &value)
Convert the provided value, for field fieldIdxInLayer.
virtual QgsVectorFileWriter::FieldValueConverter * clone() const
Creates a clone of the FieldValueConverter.
virtual QgsField fieldDefinition(const QgsField &field)
Returns a possibly modified field definition.
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...
Describes an available option for configuring file writing for a particular output format.
QgsVectorFileWriter::OptionType type
Options to pass to QgsVectorFileWriter::writeAsVectorFormat().
bool forceMulti
Sets to true to force creation of multipart geometries.
QgsCoordinateTransform ct
Transform to reproject exported geometries with, or invalid transform for no transformation.
QStringList attributesExportNames
Attributes export names.
QgsLayerMetadata layerMetadata
Layer metadata to save for the exported vector file.
QString layerName
Layer name. If let empty, it will be derived from the filename.
QgsRectangle filterExtent
If not empty, only features intersecting the extent will be saved.
bool includeConstraints
Set to true to transfer field constraints to the exported vector file.
const QgsAbstractDatabaseProviderConnection * sourceDatabaseProviderConnection
Source database provider connection, for field domains.
QgsVectorFileWriter::FieldValueConverter * fieldValueConverter
Field value converter.
QStringList layerOptions
List of OGR layer creation options.
Qgis::WkbType overrideGeometryType
Set to a valid geometry type to override the default geometry type for the layer.
bool includeZ
Sets to true to include z dimension in output. This option is only valid if overrideGeometryType is s...
Qgis::FeatureSymbologyExport symbologyExport
Symbology to export.
bool saveMetadata
Set to true to save layer metadata for the exported vector file.
QgsVectorFileWriter::ActionOnExistingFile actionOnExistingFile
Action on existing file.
QgsAttributeList attributes
Attributes to export (empty means all unless skipAttributeCreation is set)
bool onlySelectedFeatures
Write only selected features of layer.
QgsVectorFileWriter::FieldNameSource fieldNameSource
Source for exported field names.
bool skipAttributeCreation
Only write geometries.
bool setFieldDomains
Set to true to transfer field domains to the exported vector file.
QStringList datasourceOptions
List of OGR data source creation options.
QgsFeedback * feedback
Optional feedback object allowing cancellation of layer save.
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.
static QgsVectorFileWriter::WriterError writeAsVectorFormatV3(QgsVectorLayer *layer, const QString &fileName, const QgsCoordinateTransformContext &transformContext, const QgsVectorFileWriter::SaveVectorOptions &options, QString *errorMessage=nullptr, QString *newFilename=nullptr, QString *newLayer=nullptr)
Writes a layer out to a vector file.
Qgis::FeatureSymbologyExport mSymbologyExport
QString lastError() const override
Returns the most recent error encountered by the sink, e.g.
@ CanAddNewFieldsToExistingLayer
Flag to indicate that new fields can be added to an existing layer. Imply CanAppendToExistingLayer.
@ 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.
bool addFeature(QgsFeature &feature, QgsFeatureSink::Flags flags=QgsFeatureSink::Flags()) override
Adds a single feature to the sink.
static bool supportsFeatureStyles(const QString &driverName)
Returns true if the specified driverName supports feature styles.
Qgis::WkbType mWkbType
Geometry type which is being used.
bool addFeatures(QgsFeatureList &features, QgsFeatureSink::Flags flags=QgsFeatureSink::Flags()) override
Adds a list of features to the sink.
double mSymbologyScale
Scale for symbology export (e.g. for symbols units in map units)
QMap< int, int > mAttrIdxToOgrIdx
Map attribute indizes to OGR field indexes.
@ Canceled
Writing was interrupted by manual cancellation.
@ ErrSavingMetadata
Metadata saving failed.
gdal::ogr_datasource_unique_ptr mDS
static Q_DECL_DEPRECATED QgsVectorFileWriter::WriterError writeAsVectorFormatV2(QgsVectorLayer *layer, const QString &fileName, const QgsCoordinateTransformContext &transformContext, const QgsVectorFileWriter::SaveVectorOptions &options, QString *newFilename=nullptr, QString *newLayer=nullptr, QString *errorMessage=nullptr)
Writes a layer out to a vector file.
OGRGeometryH createEmptyGeometry(Qgis::WkbType wkbType)
QFlags< EditionCapability > EditionCapabilities
Combination of CanAddNewLayer, CanAppendToExistingLayer, CanAddNewFieldsToExistingLayer or CanDeleteL...
~QgsVectorFileWriter() override
Close opened shapefile for writing.
static bool targetLayerExists(const QString &datasetName, const QString &layerName)
Returns whether the target layer already exists.
double symbologyScale() const
Returns the reference scale for output.
static QStringList defaultLayerOptions(const QString &driverName)
Returns a list of the default layer options for a specified driver.
static QString driverForExtension(const QString &extension)
Returns the OGR driver name for a specified file extension.
Qgis::VectorFileWriterCapabilities capabilities() const
Returns the capabilities supported by the writer.
static QList< QgsVectorFileWriter::FilterFormatDetails > supportedFiltersAndFormats(VectorFormatOptions options=SortRecommended)
Returns a list or pairs, with format filter string as first element and OGR format key as second elem...
OGRSpatialReferenceH mOgrRef
static bool driverMetadata(const QString &driverName, MetaData &driverMetadata)
static QgsVectorFileWriter * create(const QString &fileName, const QgsFields &fields, Qgis::WkbType geometryType, const QgsCoordinateReferenceSystem &srs, const QgsCoordinateTransformContext &transformContext, const QgsVectorFileWriter::SaveVectorOptions &options, QgsFeatureSink::SinkFlags sinkFlags=QgsFeatureSink::SinkFlags(), QString *newFilename=nullptr, QString *newLayer=nullptr)
Create a new vector file writer.
QString driver() const
Returns the GDAL (short) driver name associated with the output file.
static bool deleteShapeFile(const QString &fileName)
Delete a shapefile (and its accompanying shx / dbf / prj / qix / qpj / cpg / sbn / sbx / idm / ind)
Q_DECL_DEPRECATED QgsVectorFileWriter(const QString &vectorFileName, const QString &fileEncoding, const QgsFields &fields, Qgis::WkbType geometryType, const QgsCoordinateReferenceSystem &srs=QgsCoordinateReferenceSystem(), const QString &driverName="GPKG", const QStringList &datasourceOptions=QStringList(), const QStringList &layerOptions=QStringList(), QString *newFilename=nullptr, Qgis::FeatureSymbologyExport symbologyExport=Qgis::FeatureSymbologyExport::NoSymbology, QgsFeatureSink::SinkFlags sinkFlags=QgsFeatureSink::SinkFlags(), QString *newLayer=nullptr, const QgsCoordinateTransformContext &transformContext=QgsCoordinateTransformContext(), FieldNameSource fieldNameSource=Original)
Create a new vector file writer.
static Q_DECL_DEPRECATED QgsVectorFileWriter::WriterError writeAsVectorFormat(QgsVectorLayer *layer, const QString &fileName, const QString &fileEncoding, const QgsCoordinateReferenceSystem &destCRS=QgsCoordinateReferenceSystem(), const QString &driverName="GPKG", bool onlySelected=false, QString *errorMessage=nullptr, const QStringList &datasourceOptions=QStringList(), const QStringList &layerOptions=QStringList(), bool skipAttributeCreation=false, QString *newFilename=nullptr, Qgis::FeatureSymbologyExport symbologyExport=Qgis::FeatureSymbologyExport::NoSymbology, double symbologyScale=1.0, const QgsRectangle *filterExtent=nullptr, Qgis::WkbType overrideGeometryType=Qgis::WkbType::Unknown, bool forceMulti=false, bool includeZ=false, const QgsAttributeList &attributes=QgsAttributeList(), QgsVectorFileWriter::FieldValueConverter *fieldValueConverter=nullptr, QString *newLayer=nullptr)
Write contents of vector layer to an (OGR supported) vector format.
static QString filterForDriver(const QString &driverName)
Creates a filter for an OGR driver key.
QgsVectorFileWriter::WriterError hasError() const
Checks whether there were any errors in constructor.
@ SupportsMultipleLayers
Filter to only formats which support multiple layers.
@ SkipNonSpatialFormats
Filter out any formats which do not have spatial support (e.g. those which cannot save geometries)
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.
QString driverLongName() const
Returns the GDAL long driver name associated with the output file.
QFlags< VectorFormatOption > VectorFormatOptions
WriterError mError
Contains error value if construction was not successful.
Qgis::FeatureSymbologyExport symbologyExport() const
Returns the feature symbology export handling for the writer.
FieldNameSource
Source for exported field names.
@ PreferAlias
Use the field alias as the exported field name, wherever one is set. Otherwise use the original field...
@ Original
Use original field names.
bool mIncludeConstraints
Whether to transfer field constraints to output.
static QStringList defaultDatasetOptions(const QString &driverName)
Returns a list of the default dataset options for a specified driver.
bool addFeatureWithStyle(QgsFeature &feature, QgsFeatureRenderer *renderer, Qgis::DistanceUnit outputUnit=Qgis::DistanceUnit::Meters)
Adds a feature to the currently opened data source, using the style from a specified renderer.
static QStringList supportedFormatExtensions(VectorFormatOptions options=SortRecommended)
Returns a list of file extensions for supported formats, e.g "shp", "gpkg".
bool mSetFieldDomains
Whether to set field domains to output.
static QString convertCodecNameForEncodingOption(const QString &codecName)
Converts codec name to string passed to ENCODING layer creation option of OGR Shapefile.
FieldValueConverter * mFieldValueConverter
Field value converter.
QString errorMessage() const
Retrieves error message.
void setSymbologyScale(double scale)
Set reference scale for output.
static OGRwkbGeometryType ogrTypeFromWkbType(Qgis::WkbType type)
Gets the ogr geometry type from an internal QGIS wkb type enum.
QMap< QgsSymbolLayer *, QString > mSymbolLayerTable
static QString fileFilterString(VectorFormatOptions options=SortRecommended)
Returns filter string 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.
Represents a vector layer which manages a vector based dataset.
long long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
int selectedFeatureCount() const
Returns the number of features that are selected in this layer.
Q_INVOKABLE const QgsFeatureIds & selectedFeatureIds() const
Returns a list of the selected features IDs in this layer.
QString storageType() const
Returns the permanent storage type for this layer as a friendly name.
Q_INVOKABLE Qgis::WkbType wkbType() const FINAL
Returns the WKBType or WKBUnknown in case of error.
QgsFeatureRenderer * renderer()
Returns the feature renderer used for rendering the features in the layer in 2D map views.
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider, it may be nullptr.
void minimumAndMaximumValue(int index, QVariant &minimum, QVariant &maximum) const
Calculates both the minimum and maximum value for an attribute column.
static Qgis::WkbType to25D(Qgis::WkbType type)
Will convert the 25D version of the flat type if supported or Unknown if not supported.
static bool isMultiType(Qgis::WkbType type)
Returns true if the WKB type is a multi type.
static Qgis::WkbType addZ(Qgis::WkbType type)
Adds the z dimension to a WKB type and returns the new type.
static Qgis::WkbType singleType(Qgis::WkbType type)
Returns the single type for a WKB type.
Definition qgswkbtypes.h:53
static bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
static bool hasM(Qgis::WkbType type)
Tests whether a WKB type contains m values.
static Qgis::WkbType multiType(Qgis::WkbType type)
Returns the multi type for a WKB type.
static Qgis::WkbType flatType(Qgis::WkbType type)
Returns the flat type for a WKB type.
std::unique_ptr< std::remove_pointer< OGRFeatureH >::type, OGRFeatureDeleter > ogr_feature_unique_ptr
Scoped OGR feature.
std::unique_ptr< std::remove_pointer< OGRDataSourceH >::type, OGRDataSourceDeleter > ogr_datasource_unique_ptr
Scoped OGR data source.
std::unique_ptr< std::remove_pointer< OGRFieldDefnH >::type, OGRFldDeleter > ogr_field_def_unique_ptr
Scoped OGR field definition.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
#define Q_NOWARN_DEPRECATED_POP
Definition qgis.h:6820
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:6819
QList< QgsFeature > QgsFeatureList
QList< int > QgsAttributeList
Definition qgsfield.h:27
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:41
#define QgsDebugError(str)
Definition qgslogger.h:40
QList< QgsSymbolLevel > QgsSymbolLevelOrder
Definition qgsrenderer.h:92
QList< QgsSymbolLevelItem > QgsSymbolLevel
Definition qgsrenderer.h:88
QList< QgsSymbol * > QgsSymbolList
Definition qgsrenderer.h:47
QStringList multiLayerFormats()
Details of available driver formats.
QString longName
Descriptive, user friendly name for the driver.
QString driverName
Unique driver name.
Details of available filters and formats.
QString filterString
Filter string for file picker dialogs.
QStringList globs
Matching glob patterns for format, e.g.
QMap< QString, QgsVectorFileWriter::Option * > driverOptions
QMap< QString, QgsVectorFileWriter::Option * > layerOptions