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