QGIS API Documentation  3.21.0-Master (5b68dc587e)
qgsvectorfilewriter.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsvectorfilewriter.cpp
3  generic vector file writer
4  -------------------
5  begin : Sat Jun 16 2004
6  copyright : (C) 2004 by Tim Sutton
7  email : tim at linfiniti.com
8  ***************************************************************************/
9 
10 /***************************************************************************
11  * *
12  * This program is free software; you can redistribute it and/or modify *
13  * it under the terms of the GNU General Public License as published by *
14  * the Free Software Foundation; either version 2 of the License, or *
15  * (at your option) any later version. *
16  * *
17  ***************************************************************************/
18 
19 #include "qgsapplication.h"
20 #include "qgsfields.h"
21 #include "qgsfeature.h"
22 #include "qgsfeatureiterator.h"
23 #include "qgsgeometry.h"
24 #include "qgslogger.h"
25 #include "qgsmessagelog.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"
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 
62 {
63  return field;
64 }
65 
66 QVariant QgsVectorFileWriter::FieldValueConverter::convert( int /*fieldIdxInLayer*/, const QVariant &value )
67 {
68  return value;
69 }
70 
72 {
73  return new FieldValueConverter( *this );
74 }
75 
77  const QString &vectorFileName,
78  const QString &fileEncoding,
79  const QgsFields &fields,
80  QgsWkbTypes::Type geometryType,
82  const QString &driverName,
83  const QStringList &datasourceOptions,
84  const QStringList &layerOptions,
85  QString *newFilename,
87  QgsFeatureSink::SinkFlags sinkFlags,
88  QString *newLayer,
89  const QgsCoordinateTransformContext &transformContext,
90  FieldNameSource fieldNameSource
91 )
92  : mError( NoError )
93  , mWkbType( geometryType )
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 
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 
132  const QString &fileName,
133  const QgsFields &fields,
134  QgsWkbTypes::Type geometryType,
135  const QgsCoordinateReferenceSystem &srs,
136  const QgsCoordinateTransformContext &transformContext,
138  QgsFeatureSink::SinkFlags sinkFlags,
139  QString *newFilename,
140  QString *newLayer
141 )
142 {
144  return new QgsVectorFileWriter( fileName, options.fileEncoding, fields, geometryType, srs,
145  options.driverName, options.datasourceOptions, options.layerOptions,
146  newFilename, options.symbologyExport, options.fieldValueConverter, options.layerName,
147  options.actionOnExistingFile, newLayer, transformContext, sinkFlags, options.fieldNameSource );
149 }
150 
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 
168 void QgsVectorFileWriter::init( QString vectorFileName,
169  QString fileEncoding,
170  const QgsFields &fields,
171  QgsWkbTypes::Type geometryType,
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" );
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  }
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;
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() ) );
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  {
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  {
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
428  mCoordinateTransform.reset( new QgsCoordinateTransform( srs, wgs84, transformContext ) );
429  srs = wgs84;
430  }
431  }
432 
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 
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 
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() ) );
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  {
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
760 
761  default:
762  //assert(0 && "invalid variant type!");
763  mErrorMessage = QObject::tr( "Unsupported type for field %1" )
764  .arg( attrField.name() );
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() );
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() ) );
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() ) );
844  return;
845  }
846  }
847 
848  existingIdxs.insert( ogrIdx );
849  mAttrIdxToOgrIdx.insert( fldIdx, ogrIdx );
850  }
851  }
852  break;
853 
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 
894 {
895  return OGR_G_CreateGeometry( ogrTypeFromWkbType( wkbType ) );
896 }
897 
899 class QgsVectorFileWriterMetadataContainer
900 {
901  public:
902 
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" ),
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" ),
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" ),
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" ),
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" ),
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" ),
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" ),
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" ),
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" ),
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" ),
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" ),
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" ),
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" ),
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" ),
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" ),
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" ),
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" ),
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" ),
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" ),
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" ),
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" ),
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" ),
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" ),
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" ),
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" ),
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" ),
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" ),
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" ),
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" ),
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" ),
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;
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 };
2270 
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 
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 
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 
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 
2326 {
2327  return mError;
2328 }
2329 
2331 {
2332  return mErrorMessage;
2333 }
2334 
2335 bool QgsVectorFileWriter::addFeature( QgsFeature &feature, QgsFeatureSink::Flags )
2336 {
2337  return addFeatureWithStyle( feature, nullptr, QgsUnitTypes::DistanceMeters );
2338 }
2339 
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 
2352 {
2353  return mErrorMessage;
2354 }
2355 
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 
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 
2412  {
2413  if ( !writeFeature( mLayer, poFeature.get() ) )
2414  {
2415  return false;
2416  }
2417  }
2418 
2419  return true;
2420 }
2421 
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  qint64 fid = FID_TO_NUMBER( feature.id() );
2430  if ( fid > std::numeric_limits<int>::max() )
2431  {
2432  QgsDebugMsg( QStringLiteral( "feature id %1 too large." ).arg( fid ) );
2433  OGRErr err = OGR_F_SetFID( poFeature.get(), static_cast<long>( fid ) );
2434  if ( err != OGRERR_NONE )
2435  {
2436  QgsDebugMsg( QStringLiteral( "Failed to set feature id to %1: %2 (OGR error: %3)" )
2437  .arg( feature.id() )
2438  .arg( err ).arg( CPLGetLastErrorMsg() )
2439  );
2440  }
2441  }
2442 
2443  // attribute handling
2444  for ( QMap<int, int>::const_iterator it = mAttrIdxToOgrIdx.constBegin(); it != mAttrIdxToOgrIdx.constEnd(); ++it )
2445  {
2446  int fldIdx = it.key();
2447  int ogrField = it.value();
2448 
2449  QVariant attrValue = feature.attribute( fldIdx );
2450  QgsField field = mFields.at( fldIdx );
2451 
2452  if ( !attrValue.isValid() || attrValue.isNull() )
2453  {
2454 // Starting with GDAL 2.2, there are 2 concepts: unset fields and null fields
2455 // whereas previously there was only unset fields. For a GeoJSON output,
2456 // leaving a field unset will cause it to not appear at all in the output
2457 // feature.
2458 // When all features of a layer have a field unset, this would cause the
2459 // field to not be present at all in the output, and thus on reading to
2460 // have disappeared. #16812
2461 #ifdef OGRNullMarker
2462  OGR_F_SetFieldNull( poFeature.get(), ogrField );
2463 #endif
2464  continue;
2465  }
2466 
2467  if ( mFieldValueConverter )
2468  {
2470  attrValue = mFieldValueConverter->convert( fldIdx, attrValue );
2471  }
2472 
2473  // Check type compatibility before passing attribute value to OGR
2474  QString errorMessage;
2475  if ( ! field.convertCompatible( attrValue, &errorMessage ) )
2476  {
2477  mErrorMessage = QObject::tr( "Error converting value (%1) for attribute field %2: %3" )
2478  .arg( feature.attribute( fldIdx ).toString(),
2479  mFields.at( fldIdx ).name(), errorMessage );
2480  QgsMessageLog::logMessage( mErrorMessage, QObject::tr( "OGR" ) );
2482  return nullptr;
2483  }
2484 
2485  switch ( field.type() )
2486  {
2487  case QVariant::Int:
2488  OGR_F_SetFieldInteger( poFeature.get(), ogrField, attrValue.toInt() );
2489  break;
2490  case QVariant::LongLong:
2491  OGR_F_SetFieldInteger64( poFeature.get(), ogrField, attrValue.toLongLong() );
2492  break;
2493  case QVariant::Bool:
2494  OGR_F_SetFieldInteger( poFeature.get(), ogrField, attrValue.toInt() );
2495  break;
2496  case QVariant::String:
2497  OGR_F_SetFieldString( poFeature.get(), ogrField, mCodec->fromUnicode( attrValue.toString() ).constData() );
2498  break;
2499  case QVariant::Double:
2500  OGR_F_SetFieldDouble( poFeature.get(), ogrField, attrValue.toDouble() );
2501  break;
2502  case QVariant::Date:
2503  OGR_F_SetFieldDateTime( poFeature.get(), ogrField,
2504  attrValue.toDate().year(),
2505  attrValue.toDate().month(),
2506  attrValue.toDate().day(),
2507  0, 0, 0, 0 );
2508  break;
2509  case QVariant::DateTime:
2510  if ( mOgrDriverName == QLatin1String( "ESRI Shapefile" ) )
2511  {
2512  OGR_F_SetFieldString( poFeature.get(), ogrField, mCodec->fromUnicode( attrValue.toDateTime().toString( QStringLiteral( "yyyy/MM/dd hh:mm:ss.zzz" ) ) ).constData() );
2513  }
2514  else
2515  {
2516  OGR_F_SetFieldDateTime( poFeature.get(), ogrField,
2517  attrValue.toDateTime().date().year(),
2518  attrValue.toDateTime().date().month(),
2519  attrValue.toDateTime().date().day(),
2520  attrValue.toDateTime().time().hour(),
2521  attrValue.toDateTime().time().minute(),
2522  attrValue.toDateTime().time().second(),
2523  0 );
2524  }
2525  break;
2526  case QVariant::Time:
2527  if ( mOgrDriverName == QLatin1String( "ESRI Shapefile" ) )
2528  {
2529  OGR_F_SetFieldString( poFeature.get(), ogrField, mCodec->fromUnicode( attrValue.toString() ).constData() );
2530  }
2531  else
2532  {
2533  OGR_F_SetFieldDateTime( poFeature.get(), ogrField,
2534  0, 0, 0,
2535  attrValue.toTime().hour(),
2536  attrValue.toTime().minute(),
2537  attrValue.toTime().second(),
2538  0 );
2539  }
2540  break;
2541 
2542  case QVariant::ByteArray:
2543  {
2544  const QByteArray ba = attrValue.toByteArray();
2545  OGR_F_SetFieldBinary( poFeature.get(), ogrField, ba.size(), const_cast< GByte * >( reinterpret_cast< const GByte * >( ba.data() ) ) );
2546  break;
2547  }
2548 
2549  case QVariant::Invalid:
2550  break;
2551 
2552  case QVariant::StringList:
2553  {
2554  QStringList list = attrValue.toStringList();
2555  if ( mSupportedListSubTypes.contains( QVariant::String ) )
2556  {
2557  int count = list.count();
2558  char **lst = new char *[count + 1];
2559  if ( count > 0 )
2560  {
2561  int pos = 0;
2562  for ( const QString &string : list )
2563  {
2564  lst[pos] = CPLStrdup( mCodec->fromUnicode( string ).data() );
2565  pos++;
2566  }
2567  }
2568  lst[count] = nullptr;
2569  OGR_F_SetFieldStringList( poFeature.get(), ogrField, lst );
2570  CSLDestroy( lst );
2571  }
2572  else
2573  {
2574  OGR_F_SetFieldString( poFeature.get(), ogrField, mCodec->fromUnicode( list.join( ',' ) ).constData() );
2575  }
2576  break;
2577  }
2578 
2579  case QVariant::List:
2580  // fall through to default for unsupported types
2581  if ( field.subType() == QVariant::String )
2582  {
2583  QStringList list = attrValue.toStringList();
2584  if ( mSupportedListSubTypes.contains( QVariant::String ) )
2585  {
2586  int count = list.count();
2587  char **lst = new char *[count + 1];
2588  if ( count > 0 )
2589  {
2590  int pos = 0;
2591  for ( const QString &string : list )
2592  {
2593  lst[pos] = CPLStrdup( mCodec->fromUnicode( string ).data() );
2594  pos++;
2595  }
2596  }
2597  lst[count] = nullptr;
2598  OGR_F_SetFieldStringList( poFeature.get(), ogrField, lst );
2599  CSLDestroy( lst );
2600  }
2601  else
2602  {
2603  OGR_F_SetFieldString( poFeature.get(), ogrField, mCodec->fromUnicode( list.join( ',' ) ).constData() );
2604  }
2605  break;
2606  }
2607  else if ( field.subType() == QVariant::Int )
2608  {
2609  const QVariantList list = attrValue.toList();
2610  if ( mSupportedListSubTypes.contains( QVariant::Int ) )
2611  {
2612  const int count = list.count();
2613  int *lst = new int[count];
2614  if ( count > 0 )
2615  {
2616  int pos = 0;
2617  for ( const QVariant &value : list )
2618  {
2619  lst[pos] = value.toInt();
2620  pos++;
2621  }
2622  }
2623  OGR_F_SetFieldIntegerList( poFeature.get(), ogrField, count, lst );
2624  delete [] lst;
2625  }
2626  else
2627  {
2628  QStringList strings;
2629  strings.reserve( list.size() );
2630  for ( const QVariant &value : list )
2631  {
2632  strings << QString::number( value.toInt() );
2633  }
2634  OGR_F_SetFieldString( poFeature.get(), ogrField, mCodec->fromUnicode( strings.join( ',' ) ).constData() );
2635  }
2636  break;
2637  }
2638  else if ( field.subType() == QVariant::Double )
2639  {
2640  const QVariantList list = attrValue.toList();
2641  if ( mSupportedListSubTypes.contains( QVariant::Double ) )
2642  {
2643  const int count = list.count();
2644  double *lst = new double[count];
2645  if ( count > 0 )
2646  {
2647  int pos = 0;
2648  for ( const QVariant &value : list )
2649  {
2650  lst[pos] = value.toDouble();
2651  pos++;
2652  }
2653  }
2654  OGR_F_SetFieldDoubleList( poFeature.get(), ogrField, count, lst );
2655  delete [] lst;
2656  }
2657  else
2658  {
2659  QStringList strings;
2660  strings.reserve( list.size() );
2661  for ( const QVariant &value : list )
2662  {
2663  strings << QString::number( value.toDouble() );
2664  }
2665  OGR_F_SetFieldString( poFeature.get(), ogrField, mCodec->fromUnicode( strings.join( ',' ) ).constData() );
2666  }
2667  break;
2668  }
2669  else if ( field.subType() == QVariant::LongLong )
2670  {
2671  const QVariantList list = attrValue.toList();
2672  if ( mSupportedListSubTypes.contains( QVariant::LongLong ) )
2673  {
2674  const int count = list.count();
2675  long long *lst = new long long[count];
2676  if ( count > 0 )
2677  {
2678  int pos = 0;
2679  for ( const QVariant &value : list )
2680  {
2681  lst[pos] = value.toLongLong();
2682  pos++;
2683  }
2684  }
2685  OGR_F_SetFieldInteger64List( poFeature.get(), ogrField, count, lst );
2686  delete [] lst;
2687  }
2688  else
2689  {
2690  QStringList strings;
2691  strings.reserve( list.size() );
2692  for ( const QVariant &value : list )
2693  {
2694  strings << QString::number( value.toLongLong() );
2695  }
2696  OGR_F_SetFieldString( poFeature.get(), ogrField, mCodec->fromUnicode( strings.join( ',' ) ).constData() );
2697  }
2698  break;
2699  }
2700  //intentional fall-through
2701  FALLTHROUGH
2702 
2703  default:
2704  mErrorMessage = QObject::tr( "Invalid variant type for field %1[%2]: received %3 with type %4" )
2705  .arg( mFields.at( fldIdx ).name() )
2706  .arg( ogrField )
2707  .arg( attrValue.typeName(),
2708  attrValue.toString() );
2709  QgsMessageLog::logMessage( mErrorMessage, QObject::tr( "OGR" ) );
2711  return nullptr;
2712  }
2713  }
2714 
2716  {
2717  if ( feature.hasGeometry() )
2718  {
2719  // build geometry from WKB
2720  QgsGeometry geom = feature.geometry();
2721  if ( mCoordinateTransform )
2722  {
2723  // output dataset requires coordinate transform
2724  try
2725  {
2726  geom.transform( *mCoordinateTransform );
2727  }
2728  catch ( QgsCsException & )
2729  {
2730  QgsLogger::warning( QObject::tr( "Feature geometry failed to transform" ) );
2731  return nullptr;
2732  }
2733  }
2734 
2735  // turn single geometry to multi geometry if needed
2738  {
2739  geom.convertToMultiType();
2740  }
2741 
2742  if ( geom.wkbType() != mWkbType )
2743  {
2744  OGRGeometryH mGeom2 = nullptr;
2745 
2746  // If requested WKB type is 25D and geometry WKB type is 3D,
2747  // we must force the use of 25D.
2749  {
2750  //ND: I suspect there's a bug here, in that this is NOT converting the geometry's WKB type,
2751  //so the exported WKB has a different type to what the OGRGeometry is expecting.
2752  //possibly this is handled already in OGR, but it should be fixed regardless by actually converting
2753  //geom to the correct WKB type
2754  QgsWkbTypes::Type wkbType = geom.wkbType();
2755  if ( wkbType >= QgsWkbTypes::PointZ && wkbType <= QgsWkbTypes::MultiPolygonZ )
2756  {
2758  mGeom2 = createEmptyGeometry( wkbType25d );
2759  }
2760  }
2761 
2762  // drop m/z value if not present in output wkb type
2763  if ( !QgsWkbTypes::hasZ( mWkbType ) && QgsWkbTypes::hasZ( geom.wkbType() ) )
2764  geom.get()->dropZValue();
2765  if ( !QgsWkbTypes::hasM( mWkbType ) && QgsWkbTypes::hasM( geom.wkbType() ) )
2766  geom.get()->dropMValue();
2767 
2768  // add m/z values if not present in the input wkb type -- this is needed for formats which determine
2769  // geometry type based on features, e.g. geojson
2770  if ( QgsWkbTypes::hasZ( mWkbType ) && !QgsWkbTypes::hasZ( geom.wkbType() ) )
2771  geom.get()->addZValue( 0 );
2772  if ( QgsWkbTypes::hasM( mWkbType ) && !QgsWkbTypes::hasM( geom.wkbType() ) )
2773  geom.get()->addMValue( 0 );
2774 
2775  if ( !mGeom2 )
2776  {
2777  // there's a problem when layer type is set as wkbtype Polygon
2778  // although there are also features of type MultiPolygon
2779  // (at least in OGR provider)
2780  // If the feature's wkbtype is different from the layer's wkbtype,
2781  // try to export it too.
2782  //
2783  // Btw. OGRGeometry must be exactly of the type of the geometry which it will receive
2784  // i.e. Polygons can't be imported to OGRMultiPolygon
2785  mGeom2 = createEmptyGeometry( geom.wkbType() );
2786  }
2787 
2788  if ( !mGeom2 )
2789  {
2790  mErrorMessage = QObject::tr( "Feature geometry not imported (OGR error: %1)" )
2791  .arg( QString::fromUtf8( CPLGetLastErrorMsg() ) );
2793  QgsMessageLog::logMessage( mErrorMessage, QObject::tr( "OGR" ) );
2794  return nullptr;
2795  }
2796 
2797  QByteArray wkb( geom.asWkb() );
2798  OGRErr err = OGR_G_ImportFromWkb( mGeom2, reinterpret_cast<unsigned char *>( const_cast<char *>( wkb.constData() ) ), wkb.length() );
2799  if ( err != OGRERR_NONE )
2800  {
2801  mErrorMessage = QObject::tr( "Feature geometry not imported (OGR error: %1)" )
2802  .arg( QString::fromUtf8( CPLGetLastErrorMsg() ) );
2804  QgsMessageLog::logMessage( mErrorMessage, QObject::tr( "OGR" ) );
2805  return nullptr;
2806  }
2807 
2808  // pass ownership to geometry
2809  OGR_F_SetGeometryDirectly( poFeature.get(), mGeom2 );
2810  }
2811  else // wkb type matches
2812  {
2814  OGRGeometryH ogrGeom = createEmptyGeometry( mWkbType );
2815  OGRErr err = OGR_G_ImportFromWkb( ogrGeom, reinterpret_cast<unsigned char *>( const_cast<char *>( wkb.constData() ) ), wkb.length() );
2816  if ( err != OGRERR_NONE )
2817  {
2818  mErrorMessage = QObject::tr( "Feature geometry not imported (OGR error: %1)" )
2819  .arg( QString::fromUtf8( CPLGetLastErrorMsg() ) );
2821  QgsMessageLog::logMessage( mErrorMessage, QObject::tr( "OGR" ) );
2822  return nullptr;
2823  }
2824 
2825  // set geometry (ownership is passed to OGR)
2826  OGR_F_SetGeometryDirectly( poFeature.get(), ogrGeom );
2827  }
2828  }
2829  else
2830  {
2831  OGR_F_SetGeometryDirectly( poFeature.get(), createEmptyGeometry( mWkbType ) );
2832  }
2833  }
2834  return poFeature;
2835 }
2836 
2837 void QgsVectorFileWriter::resetMap( const QgsAttributeList &attributes )
2838 {
2839  QMap<int, int> omap( mAttrIdxToOgrIdx );
2840  mAttrIdxToOgrIdx.clear();
2841  for ( int i = 0; i < attributes.size(); i++ )
2842  {
2843  if ( omap.find( i ) != omap.end() )
2844  mAttrIdxToOgrIdx.insert( attributes[i], omap[i] );
2845  }
2846 }
2847 
2848 bool QgsVectorFileWriter::writeFeature( OGRLayerH layer, OGRFeatureH feature )
2849 {
2850  if ( OGR_L_CreateFeature( layer, feature ) != OGRERR_NONE )
2851  {
2852  mErrorMessage = QObject::tr( "Feature creation error (OGR error: %1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) );
2854  QgsMessageLog::logMessage( mErrorMessage, QObject::tr( "OGR" ) );
2855  return false;
2856  }
2857  return true;
2858 }
2859 
2861 {
2862  if ( mUsingTransaction )
2863  {
2864  if ( OGRERR_NONE != OGR_L_CommitTransaction( mLayer ) )
2865  {
2866  QgsDebugMsg( QStringLiteral( "Error while committing transaction on OGRLayer." ) );
2867  }
2868  }
2869 
2870 #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,1,0) && GDAL_VERSION_NUM <= GDAL_COMPUTE_VERSION(3,1,3)
2871  if ( mDS )
2872  {
2873  // 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
2874  QString drvName = GDALGetDriverShortName( GDALGetDatasetDriver( mDS.get() ) );
2875  if ( drvName == QLatin1String( "XLSX" ) ||
2876  drvName == QLatin1String( "ODS" ) )
2877  {
2878  CPLSetThreadLocalConfigOption( "CPL_CREATE_ZIP64", "NO" );
2879  mDS.reset();
2880  CPLSetThreadLocalConfigOption( "CPL_CREATE_ZIP64", nullptr );
2881  }
2882  }
2883 #endif
2884 
2885  mDS.reset();
2886 
2887  if ( mOgrRef )
2888  {
2889  OSRRelease( mOgrRef );
2890  }
2891 }
2892 
2895  const QString &fileName,
2896  const QString &fileEncoding,
2897  const QgsCoordinateReferenceSystem &destCRS,
2898  const QString &driverName,
2899  bool onlySelected,
2900  QString *errorMessage,
2901  const QStringList &datasourceOptions,
2902  const QStringList &layerOptions,
2903  bool skipAttributeCreation,
2904  QString *newFilename,
2905  SymbologyExport symbologyExport,
2906  double symbologyScale,
2907  const QgsRectangle *filterExtent,
2908  QgsWkbTypes::Type overrideGeometryType,
2909  bool forceMulti,
2910  bool includeZ,
2911  const QgsAttributeList &attributes,
2912  FieldValueConverter *fieldValueConverter,
2913  QString *newLayer )
2914 {
2916  if ( destCRS.isValid() && layer )
2917  {
2918  ct = QgsCoordinateTransform( layer->crs(), destCRS, layer->transformContext() );
2919  }
2920 
2921  SaveVectorOptions options;
2922  options.fileEncoding = fileEncoding;
2923  options.ct = ct;
2924  options.driverName = driverName;
2925  options.onlySelectedFeatures = onlySelected;
2926  options.datasourceOptions = datasourceOptions;
2927  options.layerOptions = layerOptions;
2928  options.skipAttributeCreation = skipAttributeCreation;
2929  options.symbologyExport = symbologyExport;
2930  options.symbologyScale = symbologyScale;
2931  if ( filterExtent )
2932  options.filterExtent = *filterExtent;
2933  options.overrideGeometryType = overrideGeometryType;
2934  options.forceMulti = forceMulti;
2935  options.includeZ = includeZ;
2936  options.attributes = attributes;
2937  options.fieldValueConverter = fieldValueConverter;
2938  return writeAsVectorFormatV3( layer, fileName, layer->transformContext(), options, errorMessage, newFilename, newLayer );
2939 }
2940 
2942  const QString &fileName,
2943  const QString &fileEncoding,
2944  const QgsCoordinateTransform &ct,
2945  const QString &driverName,
2946  bool onlySelected,
2947  QString *errorMessage,
2948  const QStringList &datasourceOptions,
2949  const QStringList &layerOptions,
2950  bool skipAttributeCreation,
2951  QString *newFilename,
2952  SymbologyExport symbologyExport,
2953  double symbologyScale,
2954  const QgsRectangle *filterExtent,
2955  QgsWkbTypes::Type overrideGeometryType,
2956  bool forceMulti,
2957  bool includeZ,
2958  const QgsAttributeList &attributes,
2959  FieldValueConverter *fieldValueConverter,
2960  QString *newLayer )
2961 {
2962  SaveVectorOptions options;
2963  options.fileEncoding = fileEncoding;
2964  options.ct = ct;
2965  options.driverName = driverName;
2966  options.onlySelectedFeatures = onlySelected;
2967  options.datasourceOptions = datasourceOptions;
2968  options.layerOptions = layerOptions;
2969  options.skipAttributeCreation = skipAttributeCreation;
2970  options.symbologyExport = symbologyExport;
2971  options.symbologyScale = symbologyScale;
2972  if ( filterExtent )
2973  options.filterExtent = *filterExtent;
2974  options.overrideGeometryType = overrideGeometryType;
2975  options.forceMulti = forceMulti;
2976  options.includeZ = includeZ;
2977  options.attributes = attributes;
2978  options.fieldValueConverter = fieldValueConverter;
2979  return writeAsVectorFormatV3( layer, fileName, layer->transformContext(), options, errorMessage, newFilename, newLayer );
2980 }
2981 
2982 
2984  : driverName( QStringLiteral( "GPKG" ) )
2985 {
2986 }
2987 
2988 
2989 
2990 QgsVectorFileWriter::WriterError QgsVectorFileWriter::prepareWriteAsVectorFormat( QgsVectorLayer *layer, const QgsVectorFileWriter::SaveVectorOptions &options, QgsVectorFileWriter::PreparedWriterDetails &details )
2991 {
2992  if ( !layer || !layer->isValid() )
2993  {
2994  return ErrInvalidLayer;
2995  }
2996 
2997  if ( layer->renderer() )
2998  details.renderer.reset( layer->renderer()->clone() );
2999  details.sourceCrs = layer->crs();
3000  details.sourceWkbType = layer->wkbType();
3001  details.sourceFields = layer->fields();
3002  details.providerType = layer->providerType();
3003  details.featureCount = options.onlySelectedFeatures ? layer->selectedFeatureCount() : layer->featureCount();
3004  if ( layer->dataProvider() )
3005  details.dataSourceUri = layer->dataProvider()->dataSourceUri();
3006  details.storageType = layer->storageType();
3007  details.selectedFeatureIds = layer->selectedFeatureIds();
3008  details.providerUriParams = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
3009 
3010  if ( details.storageType == QLatin1String( "ESRI Shapefile" ) )
3011  {
3012  QgsFeatureRequest req;
3013  if ( options.onlySelectedFeatures )
3014  {
3015  req.setFilterFids( details.selectedFeatureIds );
3016  }
3017  req.setNoAttributes();
3018  details.geometryTypeScanIterator = layer->getFeatures( req );
3019  }
3020 
3021  details.expressionContext = QgsExpressionContext( QgsExpressionContextUtils::globalProjectLayerScopes( layer ) );
3022  details.renderContext.setExpressionContext( details.expressionContext );
3023  details.renderContext.setRendererScale( options.symbologyScale );
3024 
3025  details.shallTransform = false;
3026  if ( options.ct.isValid() )
3027  {
3028  // This means we should transform
3029  details.outputCrs = options.ct.destinationCrs();
3030  details.shallTransform = true;
3031  }
3032  else
3033  {
3034  // This means we shouldn't transform, use source CRS as output (if defined)
3035  details.outputCrs = details.sourceCrs;
3036  }
3037 
3038  details.destWkbType = details.sourceWkbType;
3039  if ( options.overrideGeometryType != QgsWkbTypes::Unknown )
3040  {
3041  details.destWkbType = QgsWkbTypes::flatType( options.overrideGeometryType );
3042  if ( QgsWkbTypes::hasZ( options.overrideGeometryType ) || options.includeZ )
3043  details.destWkbType = QgsWkbTypes::addZ( details.destWkbType );
3044  }
3045  if ( options.forceMulti )
3046  {
3047  details.destWkbType = QgsWkbTypes::multiType( details.destWkbType );
3048  }
3049 
3050  details.attributes = options.attributes;
3051  if ( options.skipAttributeCreation )
3052  details.attributes.clear();
3053  else if ( details.attributes.isEmpty() )
3054  {
3055  const QgsAttributeList allAttributes = details.sourceFields.allAttributesList();
3056  for ( int idx : allAttributes )
3057  {
3058  QgsField fld = details.sourceFields.at( idx );
3059  if ( details.providerType == QLatin1String( "oracle" ) && fld.typeName().contains( QLatin1String( "SDO_GEOMETRY" ) ) )
3060  continue;
3061  details.attributes.append( idx );
3062  }
3063  }
3064 
3065  if ( !details.attributes.isEmpty() )
3066  {
3067  for ( int attrIdx : std::as_const( details.attributes ) )
3068  {
3069  details.outputFields.append( details.sourceFields.at( attrIdx ) );
3070  }
3071  }
3072 
3073  // not ideal - would be nice to avoid this happening in the preparation step if possible,
3074  // but currently requires access to the layer's minimumValue/maximumValue methods
3075  if ( details.providerType == QLatin1String( "spatialite" ) )
3076  {
3077  for ( int i = 0; i < details.outputFields.size(); i++ )
3078  {
3079  if ( details.outputFields.at( i ).type() == QVariant::LongLong )
3080  {
3081  QVariant min;
3082  QVariant max;
3083  layer->minimumAndMaximumValue( i, min, max );
3084  if ( std::max( std::llabs( min.toLongLong() ), std::llabs( max.toLongLong() ) ) < std::numeric_limits<int>::max() )
3085  {
3086  details.outputFields[i].setType( QVariant::Int );
3087  }
3088  }
3089  }
3090  }
3091 
3092 
3093  //add possible attributes needed by renderer
3094  addRendererAttributes( details.renderer.get(), details.renderContext, details.sourceFields, details.attributes );
3095 
3096  QgsFeatureRequest req;
3097  req.setSubsetOfAttributes( details.attributes );
3098  if ( options.onlySelectedFeatures )
3099  req.setFilterFids( details.selectedFeatureIds );
3100 
3101  if ( !options.filterExtent.isNull() )
3102  {
3103  QgsRectangle filterRect = options.filterExtent;
3104  bool useFilterRect = true;
3105  if ( details.shallTransform )
3106  {
3107  try
3108  {
3109  // map filter rect back from destination CRS to layer CRS
3110  filterRect = options.ct.transformBoundingBox( filterRect, Qgis::TransformDirection::Reverse );
3111  }
3112  catch ( QgsCsException & )
3113  {
3114  useFilterRect = false;
3115  }
3116  }
3117  if ( useFilterRect )
3118  {
3119  req.setFilterRect( filterRect );
3120  }
3121  details.filterRectGeometry = QgsGeometry::fromRect( options.filterExtent );
3122  details.filterRectEngine.reset( QgsGeometry::createGeometryEngine( details.filterRectGeometry.constGet() ) );
3123  details.filterRectEngine->prepareGeometry();
3124  }
3125  details.sourceFeatureIterator = layer->getFeatures( req );
3126 
3127  return NoError;
3128 }
3129 
3130 QgsVectorFileWriter::WriterError QgsVectorFileWriter::writeAsVectorFormat( PreparedWriterDetails &details, const QString &fileName, const QgsVectorFileWriter::SaveVectorOptions &options, QString *newFilename, QString *errorMessage, QString *newLayer )
3131 {
3132  return writeAsVectorFormatV2( details, fileName, QgsCoordinateTransformContext(), options, newFilename, newLayer, errorMessage );
3133 }
3134 
3135 QgsVectorFileWriter::WriterError QgsVectorFileWriter::writeAsVectorFormatV2( PreparedWriterDetails &details, const QString &fileName, const QgsCoordinateTransformContext &transformContext, const QgsVectorFileWriter::SaveVectorOptions &options, QString *newFilename, QString *newLayer, QString *errorMessage )
3136 {
3137  QgsWkbTypes::Type destWkbType = details.destWkbType;
3138 
3139  int lastProgressReport = 0;
3140  long long total = details.featureCount;
3141 
3142  // Special rules for OGR layers
3143  if ( details.providerType == QLatin1String( "ogr" ) && !details.dataSourceUri.isEmpty() )
3144  {
3145  QString srcFileName( details.providerUriParams.value( QStringLiteral( "path" ) ).toString() );
3146  if ( QFile::exists( srcFileName ) && QFileInfo( fileName ).canonicalFilePath() == QFileInfo( srcFileName ).canonicalFilePath() )
3147  {
3148  // Check the layer name too if it's a GPKG/SpatiaLite/SQLite OGR driver (pay attention: camel case in layerName)
3149  QgsDataSourceUri uri( details.dataSourceUri );
3150  if ( !( ( options.driverName == QLatin1String( "GPKG" ) ||
3151  options.driverName == QLatin1String( "SpatiaLite" ) ||
3152  options.driverName == QLatin1String( "SQLite" ) ) &&
3153  options.layerName != details.providerUriParams.value( QStringLiteral( "layerName" ) ) ) )
3154  {
3155  if ( errorMessage )
3156  *errorMessage = QObject::tr( "Cannot overwrite a OGR layer in place" );
3157  return ErrCreateDataSource;
3158  }
3159  }
3160 
3161  // Shapefiles might contain multi types although wkbType() only reports singles
3162  if ( details.storageType == QLatin1String( "ESRI Shapefile" ) && !QgsWkbTypes::isMultiType( destWkbType ) )
3163  {
3164  QgsFeatureIterator fit = details.geometryTypeScanIterator;
3165  QgsFeature fet;
3166  long scanned = 0;
3167  while ( fit.nextFeature( fet ) )
3168  {
3169  if ( options.feedback && options.feedback->isCanceled() )
3170  {
3171  return Canceled;
3172  }
3173  if ( options.feedback )
3174  {
3175  //dedicate first 5% of progress bar to this scan
3176  int newProgress = static_cast<int>( ( 5.0 * scanned ) / total );
3177  if ( newProgress != lastProgressReport )
3178  {
3179  lastProgressReport = newProgress;
3180  options.feedback->setProgress( lastProgressReport );
3181  }
3182  }
3183 
3184  if ( fet.hasGeometry() && QgsWkbTypes::isMultiType( fet.geometry().wkbType() ) )
3185  {
3186  destWkbType = QgsWkbTypes::multiType( destWkbType );
3187  break;
3188  }
3189  scanned++;
3190  }
3191  }
3192  }
3193 
3194  QString tempNewFilename;
3195  QString tempNewLayer;
3196 
3197  std::unique_ptr< QgsVectorFileWriter > writer( create( fileName, details.outputFields, destWkbType, details.outputCrs, transformContext, options, QgsFeatureSink::SinkFlags(), &tempNewFilename, &tempNewLayer ) );
3198  writer->setSymbologyScale( options.symbologyScale );
3199 
3200  if ( newFilename )
3201  *newFilename = tempNewFilename;
3202 
3203  if ( newLayer )
3204  *newLayer = tempNewLayer;
3205 
3206  if ( newFilename )
3207  {
3208  QgsDebugMsgLevel( "newFilename = " + *newFilename, 2 );
3209  }
3210 
3211  // check whether file creation was successful
3212  WriterError err = writer->hasError();
3213  if ( err != NoError )
3214  {
3215  if ( errorMessage )
3216  *errorMessage = writer->errorMessage();
3217  return err;
3218  }
3219 
3220  if ( errorMessage )
3221  {
3222  errorMessage->clear();
3223  }
3224 
3225  QgsFeature fet;
3226 
3227  //create symbol table if needed
3228  if ( writer->symbologyExport() != NoSymbology )
3229  {
3230  //writer->createSymbolLayerTable( layer, writer->mDS );
3231  }
3232 
3233  if ( writer->symbologyExport() == SymbolLayerSymbology )
3234  {
3235  QgsFeatureRenderer *r = details.renderer.get();
3237  && r->usingSymbolLevels() )
3238  {
3239  QgsVectorFileWriter::WriterError error = writer->exportFeaturesSymbolLevels( details, details.sourceFeatureIterator, options.ct, errorMessage );
3240  return ( error == NoError ) ? NoError : ErrFeatureWriteFailed;
3241  }
3242  }
3243 
3244  int n = 0, errors = 0;
3245 
3246  //unit type
3247  QgsUnitTypes::DistanceUnit mapUnits = details.sourceCrs.mapUnits();
3248  if ( options.ct.isValid() )
3249  {
3250  mapUnits = options.ct.destinationCrs().mapUnits();
3251  }
3252 
3253  writer->startRender( details.renderer.get(), details.sourceFields );
3254 
3255  writer->resetMap( details.attributes );
3256  // Reset mFields to layer fields, and not just exported fields
3257  writer->mFields = details.sourceFields;
3258 
3259  // write all features
3260  long saved = 0;
3261  int initialProgress = lastProgressReport;
3262  while ( details.sourceFeatureIterator.nextFeature( fet ) )
3263  {
3264  if ( options.feedback && options.feedback->isCanceled() )
3265  {
3266  return Canceled;
3267  }
3268 
3269  saved++;
3270  if ( options.feedback )
3271  {
3272  //avoid spamming progress reports
3273  int newProgress = static_cast<int>( initialProgress + ( ( 100.0 - initialProgress ) * saved ) / total );
3274  if ( newProgress < 100 && newProgress != lastProgressReport )
3275  {
3276  lastProgressReport = newProgress;
3277  options.feedback->setProgress( lastProgressReport );
3278  }
3279  }
3280 
3281  if ( details.shallTransform )
3282  {
3283  try
3284  {
3285  if ( fet.hasGeometry() )
3286  {
3287  QgsGeometry g = fet.geometry();
3288  g.transform( options.ct );
3289  fet.setGeometry( g );
3290  }
3291  }
3292  catch ( QgsCsException &e )
3293  {
3294  QString msg = QObject::tr( "Failed to transform a point while drawing a feature with ID '%1'. Writing stopped. (Exception: %2)" )
3295  .arg( fet.id() ).arg( e.what() );
3296  QgsLogger::warning( msg );
3297  if ( errorMessage )
3298  *errorMessage = msg;
3299 
3300  return ErrProjection;
3301  }
3302  }
3303 
3304  if ( fet.hasGeometry() && details.filterRectEngine && !details.filterRectEngine->intersects( fet.geometry().constGet() ) )
3305  continue;
3306 
3307  if ( details.attributes.empty() && options.skipAttributeCreation )
3308  {
3309  fet.initAttributes( 0 );
3310  }
3311 
3312  if ( !writer->addFeatureWithStyle( fet, writer->mRenderer.get(), mapUnits ) )
3313  {
3314  WriterError err = writer->hasError();
3315  if ( err != NoError && errorMessage )
3316  {
3317  if ( errorMessage->isEmpty() )
3318  {
3319  *errorMessage = QObject::tr( "Feature write errors:" );
3320  }
3321  *errorMessage += '\n' + writer->errorMessage();
3322  }
3323  errors++;
3324 
3325  if ( errors > 1000 )
3326  {
3327  if ( errorMessage )
3328  {
3329  *errorMessage += QObject::tr( "Stopping after %1 errors" ).arg( errors );
3330  }
3331 
3332  n = -1;
3333  break;
3334  }
3335  }
3336  n++;
3337  }
3338 
3339  writer->stopRender();
3340 
3341  if ( errors > 0 && errorMessage && n > 0 )
3342  {
3343  *errorMessage += QObject::tr( "\nOnly %1 of %2 features written." ).arg( n - errors ).arg( n );
3344  }
3345 
3346  writer.reset();
3347 
3348  bool metadataFailure = false;
3349  if ( options.saveMetadata )
3350  {
3351  QString uri = QgsProviderRegistry::instance()->encodeUri( QStringLiteral( "ogr" ), QVariantMap
3352  {
3353  {QStringLiteral( "path" ), tempNewFilename },
3354  {QStringLiteral( "layerName" ), tempNewLayer }
3355  } );
3356 
3357  try
3358  {
3359  QString error;
3360  if ( !QgsProviderRegistry::instance()->saveLayerMetadata( QStringLiteral( "ogr" ), uri, options.layerMetadata, error ) )
3361  {
3362  if ( errorMessage )
3363  {
3364  if ( !errorMessage->isEmpty() )
3365  *errorMessage += '\n';
3366  *errorMessage += error;
3367  }
3368  metadataFailure = true;
3369  }
3370  }
3371  catch ( QgsNotSupportedException &e )
3372  {
3373  if ( errorMessage )
3374  {
3375  if ( !errorMessage->isEmpty() )
3376  *errorMessage += '\n';
3377  *errorMessage += e.what();
3378  }
3379  metadataFailure = true;
3380  }
3381  }
3382 
3383  return errors == 0 ? ( !metadataFailure ? NoError : ErrSavingMetadata ) : ErrFeatureWriteFailed;
3384 }
3385 
3387  const QString &fileName,
3388  const SaveVectorOptions &options,
3389  QString *newFilename,
3390  QString *errorMessage,
3391  QString *newLayer )
3392 {
3393  QgsVectorFileWriter::PreparedWriterDetails details;
3394  WriterError err = prepareWriteAsVectorFormat( layer, options, details );
3395  if ( err != NoError )
3396  return err;
3397 
3398  return writeAsVectorFormatV2( details, fileName, layer->transformContext(), options, newFilename, newLayer, errorMessage );
3399 }
3400 
3402  const QString &fileName,
3403  const QgsCoordinateTransformContext &transformContext,
3404  const SaveVectorOptions &options,
3405  QString *newFilename,
3406  QString *newLayer,
3407  QString *errorMessage )
3408 {
3409  QgsVectorFileWriter::PreparedWriterDetails details;
3410  WriterError err = prepareWriteAsVectorFormat( layer, options, details );
3411  if ( err != NoError )
3412  return err;
3413 
3414  return writeAsVectorFormatV2( details, fileName, transformContext, options, errorMessage, newFilename, newLayer );
3415 }
3416 
3417 QgsVectorFileWriter::WriterError QgsVectorFileWriter::writeAsVectorFormatV3( QgsVectorLayer *layer, const QString &fileName, const QgsCoordinateTransformContext &transformContext, const QgsVectorFileWriter::SaveVectorOptions &options, QString *errorMessage, QString *newFilename, QString *newLayer )
3418 {
3419  QgsVectorFileWriter::PreparedWriterDetails details;
3420  WriterError err = prepareWriteAsVectorFormat( layer, options, details );
3421  if ( err != NoError )
3422  return err;
3423 
3424  return writeAsVectorFormatV2( details, fileName, transformContext, options, newFilename, newLayer, errorMessage );
3425 }
3426 
3427 bool QgsVectorFileWriter::deleteShapeFile( const QString &fileName )
3428 {
3429  QFileInfo fi( fileName );
3430  QDir dir = fi.dir();
3431 
3432  QStringList filter;
3433  for ( const char *suffix : { ".shp", ".shx", ".dbf", ".prj", ".qix", ".qpj", ".cpg", ".sbn", ".sbx", ".idm", ".ind" } )
3434  {
3435  filter << fi.completeBaseName() + suffix;
3436  }
3437 
3438  bool ok = true;
3439  const auto constEntryList = dir.entryList( filter );
3440  for ( const QString &file : constEntryList )
3441  {
3442  QFile f( dir.canonicalPath() + '/' + file );
3443  if ( !f.remove() )
3444  {
3445  QgsDebugMsg( QStringLiteral( "Removing file %1 failed: %2" ).arg( file, f.errorString() ) );
3446  ok = false;
3447  }
3448  }
3449 
3450  return ok;
3451 }
3452 
3454 {
3455  mSymbologyScale = d;
3456  mRenderContext.setRendererScale( mSymbologyScale );
3457 }
3458 
3459 QList< QgsVectorFileWriter::FilterFormatDetails > QgsVectorFileWriter::supportedFiltersAndFormats( const VectorFormatOptions options )
3460 {
3461  static QReadWriteLock sFilterLock;
3462  static QMap< VectorFormatOptions, QList< QgsVectorFileWriter::FilterFormatDetails > > sFilters;
3463 
3464  QgsReadWriteLocker locker( sFilterLock, QgsReadWriteLocker::Read );
3465 
3466  const auto it = sFilters.constFind( options );
3467  if ( it != sFilters.constEnd() )
3468  return it.value();
3469 
3471  QList< QgsVectorFileWriter::FilterFormatDetails > results;
3472 
3474  int const drvCount = OGRGetDriverCount();
3475 
3476  for ( int i = 0; i < drvCount; ++i )
3477  {
3478  OGRSFDriverH drv = OGRGetDriver( i );
3479  if ( drv )
3480  {
3481  QString drvName = OGR_Dr_GetName( drv );
3482 
3483  GDALDriverH gdalDriver = GDALGetDriverByName( drvName.toLocal8Bit().constData() );
3484  char **metadata = nullptr;
3485  if ( gdalDriver )
3486  {
3487  metadata = GDALGetMetadata( gdalDriver, nullptr );
3488  }
3489 
3490  bool nonSpatialFormat = CSLFetchBoolean( metadata, GDAL_DCAP_NONSPATIAL, false );
3491 
3492  if ( OGR_Dr_TestCapability( drv, "CreateDataSource" ) != 0 )
3493  {
3494  if ( options & SkipNonSpatialFormats )
3495  {
3496  // skip non-spatial formats
3497  if ( nonSpatialFormat )
3498  continue;
3499  }
3500 
3501  QString filterString = filterForDriver( drvName );
3502  if ( filterString.isEmpty() )
3503  continue;
3504 
3505  MetaData metadata;
3506  QStringList globs;
3507  if ( driverMetadata( drvName, metadata ) && !metadata.glob.isEmpty() )
3508  {
3509  globs = metadata.glob.toLower().split( ' ' );
3510  }
3511 
3512  FilterFormatDetails details;
3513  details.driverName = drvName;
3514  details.filterString = filterString;
3515  details.globs = globs;
3516 
3517  results << details;
3518  }
3519  }
3520  }
3521 
3522  std::sort( results.begin(), results.end(), [options]( const FilterFormatDetails & a, const FilterFormatDetails & b ) -> bool
3523  {
3524  if ( options & SortRecommended )
3525  {
3526  if ( a.driverName == QLatin1String( "GPKG" ) )
3527  return true; // Make https://twitter.com/shapefiIe a sad little fellow
3528  else if ( b.driverName == QLatin1String( "GPKG" ) )
3529  return false;
3530  else if ( a.driverName == QLatin1String( "ESRI Shapefile" ) )
3531  return true;
3532  else if ( b.driverName == QLatin1String( "ESRI Shapefile" ) )
3533  return false;
3534  }
3535 
3536  return a.filterString.toLower().localeAwareCompare( b.filterString.toLower() ) < 0;
3537  } );
3538 
3539  sFilters.insert( options, results );
3540  return results;
3541 }
3542 
3543 QStringList QgsVectorFileWriter::supportedFormatExtensions( const VectorFormatOptions options )
3544 {
3545  const auto formats = supportedFiltersAndFormats( options );
3546  QSet< QString > extensions;
3547 
3548  const QRegularExpression rx( QStringLiteral( "\\*\\.(.*)$" ) );
3549 
3550  for ( const FilterFormatDetails &format : formats )
3551  {
3552  for ( const QString &glob : format.globs )
3553  {
3554  const QRegularExpressionMatch match = rx.match( glob );
3555  if ( !match.hasMatch() )
3556  continue;
3557 
3558  const QString matched = match.captured( 1 );
3559  extensions.insert( matched );
3560  }
3561  }
3562 
3563  QStringList extensionList = qgis::setToList( extensions );
3564 
3565  std::sort( extensionList.begin(), extensionList.end(), [options]( const QString & a, const QString & b ) -> bool
3566  {
3567  if ( options & SortRecommended )
3568  {
3569  if ( a == QLatin1String( "gpkg" ) )
3570  return true; // Make https://twitter.com/shapefiIe a sad little fellow
3571  else if ( b == QLatin1String( "gpkg" ) )
3572  return false;
3573  else if ( a == QLatin1String( "shp" ) )
3574  return true;
3575  else if ( b == QLatin1String( "shp" ) )
3576  return false;
3577  }
3578 
3579  return a.toLower().localeAwareCompare( b.toLower() ) < 0;
3580  } );
3581 
3582  return extensionList;
3583 }
3584 
3585 QList< QgsVectorFileWriter::DriverDetails > QgsVectorFileWriter::ogrDriverList( const VectorFormatOptions options )
3586 {
3587  QList< QgsVectorFileWriter::DriverDetails > results;
3588 
3590  const int drvCount = OGRGetDriverCount();
3591 
3592  QStringList writableDrivers;
3593  for ( int i = 0; i < drvCount; ++i )
3594  {
3595  OGRSFDriverH drv = OGRGetDriver( i );
3596  if ( drv )
3597  {
3598  QString drvName = OGR_Dr_GetName( drv );
3599 
3600  if ( options & SkipNonSpatialFormats )
3601  {
3602  // skip non-spatial formats
3603  // TODO - use GDAL metadata to determine this, when support exists in GDAL
3604  if ( drvName == QLatin1String( "ODS" ) || drvName == QLatin1String( "XLSX" ) || drvName == QLatin1String( "XLS" ) )
3605  continue;
3606  }
3607 
3608  if ( drvName == QLatin1String( "ESRI Shapefile" ) )
3609  {
3610  writableDrivers << QStringLiteral( "DBF file" );
3611  }
3612  if ( OGR_Dr_TestCapability( drv, "CreateDataSource" ) != 0 )
3613  {
3614  // Add separate format for Mapinfo MIF (MITAB is OGR default)
3615  if ( drvName == QLatin1String( "MapInfo File" ) )
3616  {
3617  writableDrivers << QStringLiteral( "MapInfo MIF" );
3618  }
3619  else if ( drvName == QLatin1String( "SQLite" ) )
3620  {
3621  // Unfortunately it seems that there is no simple way to detect if
3622  // OGR SQLite driver is compiled with SpatiaLite support.
3623  // We have HAVE_SPATIALITE in QGIS, but that may differ from OGR
3624  // http://lists.osgeo.org/pipermail/gdal-dev/2012-November/034580.html
3625  // -> test if creation fails
3626  QString option = QStringLiteral( "SPATIALITE=YES" );
3627  char *options[2] = { CPLStrdup( option.toLocal8Bit().constData() ), nullptr };
3628  OGRSFDriverH poDriver;
3630  poDriver = OGRGetDriverByName( drvName.toLocal8Bit().constData() );
3631  if ( poDriver )
3632  {
3633  gdal::ogr_datasource_unique_ptr ds( OGR_Dr_CreateDataSource( poDriver, QStringLiteral( "/vsimem/spatialitetest.sqlite" ).toUtf8().constData(), options ) );
3634  if ( ds )
3635  {
3636  writableDrivers << QStringLiteral( "SpatiaLite" );
3637  OGR_Dr_DeleteDataSource( poDriver, QStringLiteral( "/vsimem/spatialitetest.sqlite" ).toUtf8().constData() );
3638  }
3639  }
3640  CPLFree( options[0] );
3641  }
3642  writableDrivers << drvName;
3643  }
3644  }
3645  }
3646 
3647  results.reserve( writableDrivers.count() );
3648  for ( const QString &drvName : std::as_const( writableDrivers ) )
3649  {
3650  MetaData metadata;
3651  if ( driverMetadata( drvName, metadata ) && !metadata.trLongName.isEmpty() )
3652  {
3653  DriverDetails details;
3654  details.driverName = drvName;
3655  details.longName = metadata.trLongName;
3656  results << details;
3657  }
3658  }
3659 
3660  std::sort( results.begin(), results.end(), [options]( const DriverDetails & a, const DriverDetails & b ) -> bool
3661  {
3662  if ( options & SortRecommended )
3663  {
3664  if ( a.driverName == QLatin1String( "GPKG" ) )
3665  return true; // Make https://twitter.com/shapefiIe a sad little fellow
3666  else if ( b.driverName == QLatin1String( "GPKG" ) )
3667  return false;
3668  else if ( a.driverName == QLatin1String( "ESRI Shapefile" ) )
3669  return true;
3670  else if ( b.driverName == QLatin1String( "ESRI Shapefile" ) )
3671  return false;
3672  }
3673 
3674  return a.longName.toLower().localeAwareCompare( b.longName.toLower() ) < 0;
3675  } );
3676  return results;
3677 }
3678 
3679 QString QgsVectorFileWriter::driverForExtension( const QString &extension )
3680 {
3681  QString ext = extension.trimmed();
3682  if ( ext.isEmpty() )
3683  return QString();
3684 
3685  if ( ext.startsWith( '.' ) )
3686  ext.remove( 0, 1 );
3687 
3688  GDALAllRegister();
3689  int const drvCount = GDALGetDriverCount();
3690 
3691  for ( int i = 0; i < drvCount; ++i )
3692  {
3693  GDALDriverH drv = GDALGetDriver( i );
3694  if ( drv )
3695  {
3696  char **driverMetadata = GDALGetMetadata( drv, nullptr );
3697  if ( CSLFetchBoolean( driverMetadata, GDAL_DCAP_CREATE, false ) && CSLFetchBoolean( driverMetadata, GDAL_DCAP_VECTOR, false ) )
3698  {
3699  QString drvName = GDALGetDriverShortName( drv );
3700  QStringList driverExtensions = QString( GDALGetMetadataItem( drv, GDAL_DMD_EXTENSIONS, nullptr ) ).split( ' ' );
3701 
3702  const auto constDriverExtensions = driverExtensions;
3703  for ( const QString &driver : constDriverExtensions )
3704  {
3705  if ( driver.compare( ext, Qt::CaseInsensitive ) == 0 )
3706  return drvName;
3707  }
3708  }
3709  }
3710  }
3711  return QString();
3712 }
3713 
3714 QString QgsVectorFileWriter::fileFilterString( const VectorFormatOptions options )
3715 {
3716  QString filterString;
3717  const auto driverFormats = supportedFiltersAndFormats( options );
3718  for ( const FilterFormatDetails &details : driverFormats )
3719  {
3720  if ( !filterString.isEmpty() )
3721  filterString += QLatin1String( ";;" );
3722 
3723  filterString += details.filterString;
3724  }
3725  return filterString;
3726 }
3727 
3728 QString QgsVectorFileWriter::filterForDriver( const QString &driverName )
3729 {
3730  MetaData metadata;
3731  if ( !driverMetadata( driverName, metadata ) || metadata.trLongName.isEmpty() || metadata.glob.isEmpty() )
3732  return QString();
3733 
3734  return QStringLiteral( "%1 (%2 %3)" ).arg( metadata.trLongName,
3735  metadata.glob.toLower(),
3736  metadata.glob.toUpper() );
3737 }
3738 
3740 {
3741  if ( codecName == QLatin1String( "System" ) )
3742  return QStringLiteral( "LDID/0" );
3743 
3744  const QRegularExpression re( QRegularExpression::anchoredPattern( QString( "(CP|windows-|ISO[ -])(.+)" ) ), QRegularExpression::CaseInsensitiveOption );
3745  const QRegularExpressionMatch match = re.match( codecName );
3746  if ( match.hasMatch() )
3747  {
3748  QString c = match.captured( 2 ).remove( '-' );
3749  bool isNumber;
3750  ( void ) c.toInt( &isNumber );
3751  if ( isNumber )
3752  return c;
3753  }
3754  return codecName;
3755 }
3756 
3757 void QgsVectorFileWriter::createSymbolLayerTable( QgsVectorLayer *vl, const QgsCoordinateTransform &ct, OGRDataSourceH ds )
3758 {
3759  if ( !vl || !ds )
3760  {
3761  return;
3762  }
3763 
3764  QgsFeatureRenderer *renderer = vl->renderer();
3765  if ( !renderer )
3766  {
3767  return;
3768  }
3769 
3770  //unit type
3771  QgsUnitTypes::DistanceUnit mapUnits = vl->crs().mapUnits();
3772  if ( ct.isValid() )
3773  {
3774  mapUnits = ct.destinationCrs().mapUnits();
3775  }
3776 
3777  mSymbolLayerTable.clear();
3778  OGRStyleTableH ogrStyleTable = OGR_STBL_Create();
3779  OGRStyleMgrH styleManager = OGR_SM_Create( ogrStyleTable );
3780 
3781  //get symbols
3782  int nTotalLevels = 0;
3783  QgsSymbolList symbolList = renderer->symbols( mRenderContext );
3784  QgsSymbolList::iterator symbolIt = symbolList.begin();
3785  for ( ; symbolIt != symbolList.end(); ++symbolIt )
3786  {
3787  double mmsf = mmScaleFactor( mSymbologyScale, ( *symbolIt )->outputUnit(), mapUnits );
3788  double musf = mapUnitScaleFactor( mSymbologyScale, ( *symbolIt )->outputUnit(), mapUnits );
3789 
3790  int nLevels = ( *symbolIt )->symbolLayerCount();
3791  for ( int i = 0; i < nLevels; ++i )
3792  {
3793  mSymbolLayerTable.insert( ( *symbolIt )->symbolLayer( i ), QString::number( nTotalLevels ) );
3794  OGR_SM_AddStyle( styleManager, QString::number( nTotalLevels ).toLocal8Bit(),
3795  ( *symbolIt )->symbolLayer( i )->ogrFeatureStyle( mmsf, musf ).toLocal8Bit() );
3796  ++nTotalLevels;
3797  }
3798  }
3799  OGR_DS_SetStyleTableDirectly( ds, ogrStyleTable );
3800 }
3801 
3802 QgsVectorFileWriter::WriterError QgsVectorFileWriter::exportFeaturesSymbolLevels( const PreparedWriterDetails &details, QgsFeatureIterator &fit,
3803  const QgsCoordinateTransform &ct, QString *errorMessage )
3804 {
3805  if ( !details.renderer )
3806  return ErrInvalidLayer;
3807 
3808  mRenderContext.expressionContext() = details.expressionContext;
3809 
3810  QHash< QgsSymbol *, QList<QgsFeature> > features;
3811 
3812  //unit type
3813  QgsUnitTypes::DistanceUnit mapUnits = details.sourceCrs.mapUnits();
3814  if ( ct.isValid() )
3815  {
3816  mapUnits = ct.destinationCrs().mapUnits();
3817  }
3818 
3819  startRender( details.renderer.get(), details.sourceFields );
3820 
3821  //fetch features
3822  QgsFeature fet;
3823  QgsSymbol *featureSymbol = nullptr;
3824  while ( fit.nextFeature( fet ) )
3825  {
3826  if ( ct.isValid() )
3827  {
3828  try
3829  {
3830  if ( fet.hasGeometry() )
3831  {
3832  QgsGeometry g = fet.geometry();
3833  g.transform( ct );
3834  fet.setGeometry( g );
3835  }
3836  }
3837  catch ( QgsCsException &e )
3838  {
3839  QString msg = QObject::tr( "Failed to transform, writing stopped. (Exception: %1)" )
3840  .arg( e.what() );
3841  QgsLogger::warning( msg );
3842  if ( errorMessage )
3843  *errorMessage = msg;
3844 
3845  return ErrProjection;
3846  }
3847  }
3848  mRenderContext.expressionContext().setFeature( fet );
3849 
3850  featureSymbol = mRenderer->symbolForFeature( fet, mRenderContext );
3851  if ( !featureSymbol )
3852  {
3853  continue;
3854  }
3855 
3856  QHash< QgsSymbol *, QList<QgsFeature> >::iterator it = features.find( featureSymbol );
3857  if ( it == features.end() )
3858  {
3859  it = features.insert( featureSymbol, QList<QgsFeature>() );
3860  }
3861  it.value().append( fet );
3862  }
3863 
3864  //find out order
3865  QgsSymbolLevelOrder levels;
3866  QgsSymbolList symbols = mRenderer->symbols( mRenderContext );
3867  for ( int i = 0; i < symbols.count(); i++ )
3868  {
3869  QgsSymbol *sym = symbols[i];
3870  for ( int j = 0; j < sym->symbolLayerCount(); j++ )
3871  {
3872  int level = sym->symbolLayer( j )->renderingPass();
3873  if ( level < 0 || level >= 1000 ) // ignore invalid levels
3874  continue;
3875  QgsSymbolLevelItem item( sym, j );
3876  while ( level >= levels.count() ) // append new empty levels
3877  levels.append( QgsSymbolLevel() );
3878  levels[level].append( item );
3879  }
3880  }
3881 
3882  int nErrors = 0;
3883  int nTotalFeatures = 0;
3884 
3885  //export symbol layers and symbology
3886  for ( int l = 0; l < levels.count(); l++ )
3887  {
3888  QgsSymbolLevel &level = levels[l];
3889  for ( int i = 0; i < level.count(); i++ )
3890  {
3891  QgsSymbolLevelItem &item = level[i];
3892  QHash< QgsSymbol *, QList<QgsFeature> >::iterator levelIt = features.find( item.symbol() );
3893  if ( levelIt == features.end() )
3894  {
3895  ++nErrors;
3896  continue;
3897  }
3898 
3899  double mmsf = mmScaleFactor( mSymbologyScale, levelIt.key()->outputUnit(), mapUnits );
3900  double musf = mapUnitScaleFactor( mSymbologyScale, levelIt.key()->outputUnit(), mapUnits );
3901 
3902  int llayer = item.layer();
3903  QList<QgsFeature> &featureList = levelIt.value();
3904  QList<QgsFeature>::iterator featureIt = featureList.begin();
3905  for ( ; featureIt != featureList.end(); ++featureIt )
3906  {
3907  ++nTotalFeatures;
3908  gdal::ogr_feature_unique_ptr ogrFeature = createFeature( *featureIt );
3909  if ( !ogrFeature )
3910  {
3911  ++nErrors;
3912  continue;
3913  }
3914 
3915  QString styleString = levelIt.key()->symbolLayer( llayer )->ogrFeatureStyle( mmsf, musf );
3916  if ( !styleString.isEmpty() )
3917  {
3918  OGR_F_SetStyleString( ogrFeature.get(), styleString.toLocal8Bit().constData() );
3919  if ( !writeFeature( mLayer, ogrFeature.get() ) )
3920  {
3921  ++nErrors;
3922  }
3923  }
3924  }
3925  }
3926  }
3927 
3928  stopRender();
3929 
3930  if ( nErrors > 0 && errorMessage )
3931  {
3932  *errorMessage += QObject::tr( "\nOnly %1 of %2 features written." ).arg( nTotalFeatures - nErrors ).arg( nTotalFeatures );
3933  }
3934 
3936 }
3937 
3938 double QgsVectorFileWriter::mmScaleFactor( double scale, QgsUnitTypes::RenderUnit symbolUnits, QgsUnitTypes::DistanceUnit mapUnits )
3939 {
3940  if ( symbolUnits == QgsUnitTypes::RenderMillimeters )
3941  {
3942  return 1.0;
3943  }
3944  else
3945  {
3946  //conversion factor map units -> mm
3947  if ( mapUnits == QgsUnitTypes::DistanceMeters )
3948  {
3949  return 1000 / scale;
3950  }
3951 
3952  }
3953  return 1.0; //todo: map units
3954 }
3955 
3956 double QgsVectorFileWriter::mapUnitScaleFactor( double scale, QgsUnitTypes::RenderUnit symbolUnits, QgsUnitTypes::DistanceUnit mapUnits )
3957 {
3958  if ( symbolUnits == QgsUnitTypes::RenderMapUnits )
3959  {
3960  return 1.0;
3961  }
3962  else
3963  {
3964  if ( symbolUnits == QgsUnitTypes::RenderMillimeters && mapUnits == QgsUnitTypes::DistanceMeters )
3965  {
3966  return scale / 1000;
3967  }
3968  }
3969  return 1.0;
3970 }
3971 
3972 void QgsVectorFileWriter::startRender( QgsFeatureRenderer *sourceRenderer, const QgsFields &fields )
3973 {
3974  mRenderer = createSymbologyRenderer( sourceRenderer );
3975  if ( !mRenderer )
3976  {
3977  return;
3978  }
3979 
3980  mRenderer->startRender( mRenderContext, fields );
3981 }
3982 
3983 void QgsVectorFileWriter::stopRender()
3984 {
3985  if ( !mRenderer )
3986  {
3987  return;
3988  }
3989 
3990  mRenderer->stopRender( mRenderContext );
3991 }
3992 
3993 std::unique_ptr<QgsFeatureRenderer> QgsVectorFileWriter::createSymbologyRenderer( QgsFeatureRenderer *sourceRenderer ) const
3994 {
3995  if ( mSymbologyExport == NoSymbology )
3996  {
3997  return nullptr;
3998  }
3999  if ( !sourceRenderer )
4000  {
4001  return nullptr;
4002  }
4003 
4004  return std::unique_ptr< QgsFeatureRenderer >( sourceRenderer->clone() );
4005 }
4006 
4007 void QgsVectorFileWriter::addRendererAttributes( QgsFeatureRenderer *renderer, QgsRenderContext &context, const QgsFields &fields, QgsAttributeList &attList )
4008 {
4009  if ( renderer )
4010  {
4011  const QSet<QString> rendererAttributes = renderer->usedAttributes( context );
4012  for ( const QString &attr : rendererAttributes )
4013  {
4014  int index = fields.lookupField( attr );
4015  if ( index != -1 )
4016  {
4017  attList.append( index );
4018  }
4019  }
4020  }
4021 }
4022 
4023 QStringList QgsVectorFileWriter::concatenateOptions( const QMap<QString, QgsVectorFileWriter::Option *> &options )
4024 {
4025  QStringList list;
4026  QMap<QString, QgsVectorFileWriter::Option *>::ConstIterator it;
4027 
4028  for ( it = options.constBegin(); it != options.constEnd(); ++it )
4029  {
4030  QgsVectorFileWriter::Option *option = it.value();
4031  switch ( option->type )
4032  {
4034  {
4035  QgsVectorFileWriter::IntOption *opt = dynamic_cast<QgsVectorFileWriter::IntOption *>( option );
4036  if ( opt )
4037  {
4038  list.append( QStringLiteral( "%1=%2" ).arg( it.key() ).arg( opt->defaultValue ) );
4039  }
4040  break;
4041  }
4042 
4044  {
4045  QgsVectorFileWriter::SetOption *opt = dynamic_cast<QgsVectorFileWriter::SetOption *>( option );
4046  if ( opt && !opt->defaultValue.isEmpty() )
4047  {
4048  list.append( QStringLiteral( "%1=%2" ).arg( it.key(), opt->defaultValue ) );
4049  }
4050  break;
4051  }
4052 
4054  {
4056  if ( opt && !opt->defaultValue.isNull() )
4057  {
4058  list.append( QStringLiteral( "%1=%2" ).arg( it.key(), opt->defaultValue ) );
4059  }
4060  break;
4061  }
4062 
4065  if ( opt )
4066  {
4067  list.append( QStringLiteral( "%1=%2" ).arg( it.key(), opt->mValue ) );
4068  }
4069  break;
4070  }
4071  }
4072 
4073  return list;
4074 }
4075 
4076 QgsVectorFileWriter::EditionCapabilities QgsVectorFileWriter::editionCapabilities( const QString &datasetName )
4077 {
4078  OGRSFDriverH hDriver = nullptr;
4079  gdal::ogr_datasource_unique_ptr hDS( OGROpen( datasetName.toUtf8().constData(), TRUE, &hDriver ) );
4080  if ( !hDS )
4081  return QgsVectorFileWriter::EditionCapabilities();
4082  QString drvName = OGR_Dr_GetName( hDriver );
4083  QgsVectorFileWriter::EditionCapabilities caps = QgsVectorFileWriter::EditionCapabilities();
4084  if ( OGR_DS_TestCapability( hDS.get(), ODsCCreateLayer ) )
4085  {
4086  // Shapefile driver returns True for a "foo.shp" dataset name,
4087  // creating "bar.shp" new layer, but this would be a bit confusing
4088  // for the user, so pretent that it does not support that
4089  if ( !( drvName == QLatin1String( "ESRI Shapefile" ) && QFile::exists( datasetName ) ) )
4090  caps |= CanAddNewLayer;
4091  }
4092  if ( OGR_DS_TestCapability( hDS.get(), ODsCDeleteLayer ) )
4093  {
4094  caps |= CanDeleteLayer;
4095  }
4096  int layer_count = OGR_DS_GetLayerCount( hDS.get() );
4097  if ( layer_count )
4098  {
4099  OGRLayerH hLayer = OGR_DS_GetLayer( hDS.get(), 0 );
4100  if ( hLayer )
4101  {
4102  if ( OGR_L_TestCapability( hLayer, OLCSequentialWrite ) )
4103  {
4104  caps |= CanAppendToExistingLayer;
4105  if ( OGR_L_TestCapability( hLayer, OLCCreateField ) )
4106  {
4108  }
4109  }
4110  }
4111  }
4112  return caps;
4113 }
4114 
4115 bool QgsVectorFileWriter::targetLayerExists( const QString &datasetName,
4116  const QString &layerNameIn )
4117 {
4118  OGRSFDriverH hDriver = nullptr;
4119  gdal::ogr_datasource_unique_ptr hDS( OGROpen( datasetName.toUtf8().constData(), TRUE, &hDriver ) );
4120  if ( !hDS )
4121  return false;
4122 
4123  QString layerName( layerNameIn );
4124  if ( layerName.isEmpty() )
4125  layerName = QFileInfo( datasetName ).baseName();
4126 
4127  return OGR_DS_GetLayerByName( hDS.get(), layerName.toUtf8().constData() );
4128 }
4129 
4130 
4131 bool QgsVectorFileWriter::areThereNewFieldsToCreate( const QString &datasetName,
4132  const QString &layerName,
4133  QgsVectorLayer *layer,
4134  const QgsAttributeList &attributes )
4135 {
4136  OGRSFDriverH hDriver = nullptr;
4137  gdal::ogr_datasource_unique_ptr hDS( OGROpen( datasetName.toUtf8().constData(), TRUE, &hDriver ) );
4138  if ( !hDS )
4139  return false;
4140  OGRLayerH hLayer = OGR_DS_GetLayerByName( hDS.get(), layerName.toUtf8().constData() );
4141  if ( !hLayer )
4142  {
4143  return false;
4144  }
4145  bool ret = false;
4146  OGRFeatureDefnH defn = OGR_L_GetLayerDefn( hLayer );
4147  const auto constAttributes = attributes;
4148  for ( int idx : constAttributes )
4149  {
4150  QgsField fld = layer->fields().at( idx );
4151  if ( OGR_FD_GetFieldIndex( defn, fld.name().toUtf8().constData() ) < 0 )
4152  {
4153  ret = true;
4154  break;
4155  }
4156  }
4157  return ret;
4158 }
virtual bool addZValue(double zValue=0)=0
Adds a z-dimension to the geometry, initialized to a preset value.
virtual bool dropMValue()=0
Drops any measure values which exist in the geometry.
virtual bool addMValue(double mValue=0)=0
Adds a measure to the geometry, initialized to a preset value.
virtual bool dropZValue()=0
Drops any z-dimensions which exist in the geometry.
@ FlagExportTrianglesAsPolygons
Triangles should be exported as polygon geometries.
static void registerOgrDrivers()
Register OGR drivers ensuring this only happens once.
This class represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
static Q_INVOKABLE QgsCoordinateReferenceSystem fromEpsgId(long epsg)
Creates a CRS from a given EPSG ID.
QString authid() const
Returns the authority identifier for the CRS.
Q_GADGET QgsUnitTypes::DistanceUnit mapUnits
Contains information about the context in which a coordinate transform is executed.
Class for doing transforms between two map coordinate systems.
bool isValid() const
Returns true if the coordinate transform is valid, ie both the source and destination CRS have been s...
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system, which the transform will transform coordinates t...
QgsRectangle transformBoundingBox(const QgsRectangle &rectangle, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool handle180Crossover=false) const SIP_THROW(QgsCsException)
Transforms a rectangle from the source CRS to the destination CRS.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:66
virtual QString dataSourceUri(bool expandAuthConfig=false) const
Gets the data source specification.
Class for storing the component parts of a RDBMS data source URI (e.g.
QString what() const
Definition: qgsexception.h:48
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
virtual QgsFeatureRenderer * clone() const =0
Create a deep copy of this renderer.
virtual QgsSymbolList symbolsForFeature(const QgsFeature &feature, QgsRenderContext &context) const
Returns list of symbols used for rendering the feature.
virtual QgsSymbolList symbols(QgsRenderContext &context) const
Returns list of symbols used by the renderer.
bool usingSymbolLevels() const
Definition: qgsrenderer.h:292
virtual QgsFeatureRenderer::Capabilities capabilities()
Returns details about internals of this renderer.
Definition: qgsrenderer.h:283
@ SymbolLevels
Rendering with symbol levels (i.e. implements symbols(), symbolForFeature())
Definition: qgsrenderer.h:263
virtual QSet< QString > usedAttributes(const QgsRenderContext &context) const =0
Returns a list of attributes required by this renderer.
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFilterFids(const QgsFeatureIds &fids)
Sets the feature IDs that should be fetched.
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
QgsFeatureRequest & setNoAttributes()
Set that no attributes will be fetched.
QgsFeatureRequest & setFilterRect(const QgsRectangle &rectangle)
Sets the rectangle from which features will be taken.
@ RegeneratePrimaryKey
This flag indicates, that a primary key field cannot be guaranteed to be unique and the sink should i...
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
void initAttributes(int fieldCount)
Initialize this feature with the given number of fields.
Definition: qgsfeature.cpp:210
QgsGeometry geometry
Definition: qgsfeature.h:67
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:205
QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
Definition: qgsfeature.cpp:302
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
Definition: qgsfeature.cpp:145
Q_GADGET QgsFeatureId id
Definition: qgsfeature.h:64
bool isCanceled() const SIP_HOLDGIL
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:54
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition: qgsfeedback.h:63
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:51
QString typeName() const
Gets the field type.
Definition: qgsfield.cpp:138
QString name
Definition: qgsfield.h:60
int precision
Definition: qgsfield.h:57
int length
Definition: qgsfield.h:56
bool convertCompatible(QVariant &v, QString *errorMessage=nullptr) const
Converts the provided variant to a compatible format.
Definition: qgsfield.cpp:371
QVariant::Type type
Definition: qgsfield.h:58
QVariant::Type subType() const
If the field is a collection, gets its element's type.
Definition: qgsfield.cpp:133
QString alias
Definition: qgsfield.h:61
Container of fields for a vector layer.
Definition: qgsfields.h:45
int count() const
Returns number of items.
Definition: qgsfields.cpp:133
int size() const
Returns number of items.
Definition: qgsfields.cpp:138
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Definition: qgsfields.cpp:163
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
Definition: qgsfields.cpp:344
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:124
const QgsAbstractGeometry * constGet() const SIP_HOLDGIL
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
QgsWkbTypes::Type wkbType() const SIP_HOLDGIL
Returns type of the geometry as a WKB type (point / linestring / polygon etc.)
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false) SIP_THROW(QgsCsException)
Transforms this geometry as described by the coordinate transform ct.
QgsAbstractGeometry * get()
Returns a modifiable (non-const) reference to the underlying abstract geometry primitive.
static QgsGeometry fromRect(const QgsRectangle &rect) SIP_HOLDGIL
Creates a new geometry from a QgsRectangle.
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry)
Creates and returns a new geometry engine representing the specified geometry.
bool convertToMultiType()
Converts single type geometry into multitype geometry e.g.
QByteArray asWkb(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const
Export the geometry to WKB.
static void warning(const QString &msg)
Goes to qWarning.
Definition: qgslogger.cpp:122
QString providerType() const
Returns the provider type (provider key) for this layer.
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:79
QgsCoordinateTransformContext transformContext() const
Returns the layer data provider coordinate transform context or a default transform context if the la...
bool isValid
Definition: qgsmaplayer.h:81
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
Custom exception class which is raised when an operation is not supported.
Definition: qgsexception.h:118
static OGRSpatialReferenceH crsToOGRSpatialReference(const QgsCoordinateReferenceSystem &crs)
Returns a OGRSpatialReferenceH corresponding to the specified crs object.
QVariantMap decodeUri(const QString &providerKey, const QString &uri)
Breaks a provider data source URI into its component paths (e.g.
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
QString encodeUri(const QString &providerKey, const QVariantMap &parts)
Reassembles a provider data source URI from its component paths (e.g.
bool saveLayerMetadata(const QString &providerKey, const QString &uri, const QgsLayerMetadata &metadata, QString &errorMessage) SIP_THROW(QgsNotSupportedException)
Saves metadata to the layer corresponding to the specified uri.
The QgsReadWriteLocker class is a convenience class that simplifies locking and unlocking QReadWriteL...
@ Write
Lock for write.
@ Read
Lock for read.
void changeMode(Mode mode)
Change the mode of the lock to mode.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
bool isNull() const
Test if the rectangle is null (all coordinates zero or after call to setMinimal()).
Definition: qgsrectangle.h:479
Contains information about the context of a rendering operation.
QgsExpressionContext & expressionContext()
Gets the expression context.
void setRendererScale(double scale)
Sets the renderer map scale.
int renderingPass() const
Specifies the rendering pass in which this symbol layer should be rendered.
int layer() const
The layer of this symbol level.
QgsSymbol * symbol() const
The symbol of this symbol level.
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:38
QgsSymbolLayer * symbolLayer(int layer)
Returns the symbol layer at the specified index.
Definition: qgssymbol.cpp:420
int symbolLayerCount() const
Returns the total number of symbol layers contained in the symbol.
Definition: qgssymbol.h:160
DistanceUnit
Units of distance.
Definition: qgsunittypes.h:68
@ DistanceMeters
Meters.
Definition: qgsunittypes.h:69
RenderUnit
Rendering size units.
Definition: qgsunittypes.h:168
@ RenderMillimeters
Millimeters.
Definition: qgsunittypes.h:169
@ RenderMapUnits
Map units.
Definition: qgsunittypes.h:170
Interface to convert raw field values to their user-friendly value.
virtual QVariant convert(int fieldIdxInLayer, const QVariant &value)
Convert the provided value, for field fieldIdxInLayer.
virtual QgsVectorFileWriter::FieldValueConverter * clone() const
Creates a clone of the FieldValueConverter.
virtual QgsField fieldDefinition(const QgsField &field)
Returns a possibly modified field definition.
QgsVectorFileWriter::OptionType type
Options to pass to writeAsVectorFormat()
bool forceMulti
Sets to true to force creation of multi* geometries.
FieldNameSource fieldNameSource
Source for exported field names.
QgsCoordinateTransform ct
Transform to reproject exported geometries with, or invalid transform for no transformation.
QgsLayerMetadata layerMetadata
Layer metadata to save for the exported vector file.
QString layerName
Layer name. If let empty, it will be derived from the filename.
QgsRectangle filterExtent
If not empty, only features intersecting the extent will be saved.
QgsVectorFileWriter::FieldValueConverter * fieldValueConverter
Field value converter.
QStringList layerOptions
List of OGR layer creation options.
QgsVectorFileWriter::SymbologyExport symbologyExport
Symbology to export.
bool includeZ
Sets to true to include z dimension in output. This option is only valid if overrideGeometryType is s...
bool saveMetadata
Set to true to save layer metadata for the exported vector file.
QgsVectorFileWriter::ActionOnExistingFile actionOnExistingFile
Action on existing file.
QgsAttributeList attributes
Attributes to export (empty means all unless skipAttributeCreation is set)
bool onlySelectedFeatures
Write only selected features of layer.
bool skipAttributeCreation
Only write geometries.
QStringList datasourceOptions
List of OGR data source creation options.
QgsWkbTypes::Type overrideGeometryType
Set to a valid geometry type to override the default geometry type for the layer.
QgsFeedback * feedback
Optional feedback object allowing cancellation of layer save.
A convenience class for writing vector layers to disk based formats (e.g.
static QgsVectorFileWriter::WriterError writeAsVectorFormatV3(QgsVectorLayer *layer, const QString &fileName, const QgsCoordinateTransformContext &transformContext, const QgsVectorFileWriter::SaveVectorOptions &options, QString *errorMessage=nullptr, QString *newFilename=nullptr, QString *newLayer=nullptr)
Writes a layer out to a vector file.
QString lastError() const override
Returns the most recent error encountered by the sink, e.g.
@ CanAddNewFieldsToExistingLayer
Flag to indicate that new fields can be added to an existing layer. Imply CanAppendToExistingLayer.
@ CanAppendToExistingLayer
Flag to indicate that new features can be added to an existing layer.
@ CanAddNewLayer
Flag to indicate that a new layer can be added to the dataset.
@ CanDeleteLayer
Flag to indicate that an existing layer can be deleted.
static QgsVectorFileWriter::EditionCapabilities editionCapabilities(const QString &datasetName)
Returns edition capabilities for an existing dataset name.
static QgsVectorFileWriter * create(const QString &fileName, const QgsFields &fields, QgsWkbTypes::Type geometryType, const QgsCoordinateReferenceSystem &srs, const QgsCoordinateTransformContext &transformContext, const QgsVectorFileWriter::SaveVectorOptions &options, QgsFeatureSink::SinkFlags sinkFlags=QgsFeatureSink::SinkFlags(), QString *newFilename=nullptr, QString *newLayer=nullptr)
Create a new vector file writer.
bool addFeature(QgsFeature &feature, QgsFeatureSink::Flags flags=QgsFeatureSink::Flags()) override
Adds a single feature to the sink.
static bool supportsFeatureStyles(const QString &driverName)
Returns true if the specified driverName supports feature styles.
bool addFeatures(QgsFeatureList &features, QgsFeatureSink::Flags flags=QgsFeatureSink::Flags()) override
Adds a list of features to the sink.
double mSymbologyScale
Scale for symbology export (e.g. for symbols units in map units)
OGRGeometryH createEmptyGeometry(QgsWkbTypes::Type wkbType)
QMap< int, int > mAttrIdxToOgrIdx
Map attribute indizes to OGR field indexes.
@ Canceled
Writing was interrupted by manual cancellation.
@ ErrSavingMetadata
Metadata saving failed.
gdal::ogr_datasource_unique_ptr mDS
static Q_DECL_DEPRECATED QgsVectorFileWriter::WriterError writeAsVectorFormatV2(QgsVectorLayer *layer, const QString &fileName, const QgsCoordinateTransformContext &transformContext, const QgsVectorFileWriter::SaveVectorOptions &options, QString *newFilename=nullptr, QString *newLayer=nullptr, QString *errorMessage=nullptr)
Writes a layer out to a vector file.
~QgsVectorFileWriter() override
Close opened shapefile for writing.
static bool targetLayerExists(const QString &datasetName, const QString &layerName)
Returns whether the target layer already exists.
double symbologyScale() const
Returns the reference scale for output.
static QStringList defaultLayerOptions(const QString &driverName)
Returns a list of the default layer options for a specified driver.
SymbologyExport mSymbologyExport
static QString driverForExtension(const QString &extension)
Returns the OGR driver name for a specified file extension.
static QList< QgsVectorFileWriter::FilterFormatDetails > supportedFiltersAndFormats(VectorFormatOptions options=SortRecommended)
Returns a list or pairs, with format filter string as first element and OGR format key as second elem...
OGRSpatialReferenceH mOgrRef
static bool driverMetadata(const QString &driverName, MetaData &driverMetadata)
QgsVectorFileWriter::WriterError hasError()
Checks whether there were any errors in constructor.
static bool deleteShapeFile(const QString &fileName)
Delete a shapefile (and its accompanying shx / dbf / prj / qix / qpj / cpg / sbn / sbx / idm / ind)
static QString filterForDriver(const QString &driverName)
Creates a filter for an OGR driver key.
QgsWkbTypes::Type mWkbType
Geometry type which is being used.
static OGRwkbGeometryType ogrTypeFromWkbType(QgsWkbTypes::Type type)
Gets the ogr geometry type from an internal QGIS wkb type enum.
@ SkipNonSpatialFormats
Filter out any formats which do not have spatial support (e.g. those which cannot save geometries)
static bool areThereNewFieldsToCreate(const QString &datasetName, const QString &layerName, QgsVectorLayer *layer, const QgsAttributeList &attributes)
Returns whether there are among the attributes specified some that do not exist yet in the layer.
static QList< QgsVectorFileWriter::DriverDetails > ogrDriverList(VectorFormatOptions options=SortRecommended)
Returns the driver list that can be used for dialogs.
static Q_DECL_DEPRECATED QgsVectorFileWriter::WriterError writeAsVectorFormat(QgsVectorLayer *layer, const QString &fileName, const QString &fileEncoding, const QgsCoordinateReferenceSystem &destCRS=QgsCoordinateReferenceSystem(), const QString &driverName="GPKG", bool onlySelected=false, QString *errorMessage=nullptr, const QStringList &datasourceOptions=QStringList(), const QStringList &layerOptions=QStringList(), bool skipAttributeCreation=false, QString *newFilename=nullptr, QgsVectorFileWriter::SymbologyExport symbologyExport=QgsVectorFileWriter::NoSymbology, double symbologyScale=1.0, const QgsRectangle *filterExtent=nullptr, QgsWkbTypes::Type overrideGeometryType=QgsWkbTypes::Unknown, bool forceMulti=false, bool includeZ=false, const QgsAttributeList &attributes=QgsAttributeList(), QgsVectorFileWriter::FieldValueConverter *fieldValueConverter=nullptr, QString *newLayer=nullptr)
Write contents of vector layer to an (OGR supported) vector format.
WriterError mError
Contains error value if construction was not successful.
FieldNameSource
Source for exported field names.
@ PreferAlias
Use the field alias as the exported field name, wherever one is set. Otherwise use the original field...
@ Original
Use original field names.
static QStringList defaultDatasetOptions(const QString &driverName)
Returns a list of the default dataset options for a specified driver.
static QStringList supportedFormatExtensions(VectorFormatOptions options=SortRecommended)
Returns a list of file extensions for supported formats, e.g "shp", "gpkg".
QString errorMessage()
Retrieves error message.
static QString convertCodecNameForEncodingOption(const QString &codecName)
Converts codec name to string passed to ENCODING layer creation option of OGR Shapefile.
FieldValueConverter * mFieldValueConverter
Field value converter.
bool addFeatureWithStyle(QgsFeature &feature, QgsFeatureRenderer *renderer, QgsUnitTypes::DistanceUnit outputUnit=QgsUnitTypes::DistanceMeters)
Adds a feature to the currently opened data source, using the style from a specified renderer.
QgsVectorFileWriter::SymbologyExport symbologyExport() const
void setSymbologyScale(double scale)
Set reference scale for output.
Q_DECL_DEPRECATED QgsVectorFileWriter(const QString &vectorFileName, const QString &fileEncoding, const QgsFields &fields, QgsWkbTypes::Type geometryType, const QgsCoordinateReferenceSystem &srs=QgsCoordinateReferenceSystem(), const QString &driverName="GPKG", const QStringList &datasourceOptions=QStringList(), const QStringList &layerOptions=QStringList(), QString *newFilename=nullptr, QgsVectorFileWriter::SymbologyExport symbologyExport=QgsVectorFileWriter::NoSymbology, QgsFeatureSink::SinkFlags sinkFlags=QgsFeatureSink::SinkFlags(), QString *newLayer=nullptr, const QgsCoordinateTransformContext &transformContext=QgsCoordinateTransformContext(), FieldNameSource fieldNameSource=Original)
Create a new vector file writer.
QMap< QgsSymbolLayer *, QString > mSymbolLayerTable
static QString fileFilterString(VectorFormatOptions options=SortRecommended)
Returns filter string that can be used for dialogs.
ActionOnExistingFile
Combination of CanAddNewLayer, CanAppendToExistingLayer, CanAddNewFieldsToExistingLayer or CanDeleteL...
@ CreateOrOverwriteLayer
Create or overwrite layer.
@ CreateOrOverwriteFile
Create or overwrite file.
@ AppendToLayerNoNewFields
Append features to existing layer, but do not create new fields.
@ AppendToLayerAddFields
Append features to existing layer, and create new fields if needed.
Represents a vector layer which manages a vector based data sets.
Q_INVOKABLE QgsWkbTypes::Type wkbType() const FINAL
Returns the WKBType or WKBUnknown in case of error.
long long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
int selectedFeatureCount() const
Returns the number of features that are selected in this layer.
Q_INVOKABLE const QgsFeatureIds & selectedFeatureIds() const
Returns a list of the selected features IDs in this layer.
QString storageType() const
Returns the permanent storage type for this layer as a friendly name.
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider, it may be nullptr.
void minimumAndMaximumValue(int index, QVariant &minimum, QVariant &maximum) const
Calculates both the minimum and maximum value for an attribute column.
QgsFeatureRenderer * renderer()
Returns the feature renderer used for rendering the features in the layer in 2D map views.
static bool isMultiType(Type type) SIP_HOLDGIL
Returns true if the WKB type is a multi type.
Definition: qgswkbtypes.h:832
static bool hasM(Type type) SIP_HOLDGIL
Tests whether a WKB type contains m values.
Definition: qgswkbtypes.h:1100
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:70
@ GeometryCollectionZ
Definition: qgswkbtypes.h:93
static Type to25D(Type type) SIP_HOLDGIL
Will convert the 25D version of the flat type if supported or Unknown if not supported.
Definition: qgswkbtypes.h:1241
static Type singleType(Type type) SIP_HOLDGIL
Returns the single type for a WKB type.
Definition: qgswkbtypes.h:157
static Type flatType(Type type) SIP_HOLDGIL
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:702
static Type multiType(Type type) SIP_HOLDGIL
Returns the multi type for a WKB type.
Definition: qgswkbtypes.h:302
static Type addZ(Type type) SIP_HOLDGIL
Adds the z dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1146
static bool hasZ(Type type) SIP_HOLDGIL
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:1050
std::unique_ptr< std::remove_pointer< OGRFeatureH >::type, OGRFeatureDeleter > ogr_feature_unique_ptr
Scoped OGR feature.
Definition: qgsogrutils.h:133
std::unique_ptr< std::remove_pointer< OGRDataSourceH >::type, OGRDataSourceDeleter > ogr_datasource_unique_ptr
Scoped OGR data source.
Definition: qgsogrutils.h:118
std::unique_ptr< std::remove_pointer< OGRFieldDefnH >::type, OGRFldDeleter > ogr_field_def_unique_ptr
Scoped OGR field definition.
Definition: qgsogrutils.h:128
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
#define FALLTHROUGH
Definition: qgis.h:1757
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:1730
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:1729
QList< QgsFeature > QgsFeatureList
Definition: qgsfeature.h:834
#define FID_TO_NUMBER(fid)
Definition: qgsfeatureid.h:32
QList< int > QgsAttributeList
Definition: qgsfield.h:26
const QgsField & field
Definition: qgsfield.h:463
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QList< QgsSymbolLevel > QgsSymbolLevelOrder
Definition: qgsrenderer.h:88
QList< QgsSymbolLevelItem > QgsSymbolLevel
Definition: qgsrenderer.h:84
QList< QgsSymbol * > QgsSymbolList
Definition: qgsrenderer.h:44
Details of available driver formats.
QString longName
Descriptive, user friendly name for the driver.
QString driverName
Unique driver name.
Details of available filters and formats.
QString filterString
Filter string for file picker dialogs.
QStringList globs
Matching glob patterns for format, e.g.
QMap< QString, QgsVectorFileWriter::Option * > driverOptions
QMap< QString, QgsVectorFileWriter::Option * > layerOptions