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