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