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