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