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