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