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