QGIS API Documentation  3.25.0-Master (dec16ba68b)
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  if ( details.sourceFields.exists( attrIdx ) )
3108  {
3109  QgsField field = details.sourceFields.at( attrIdx );
3110  field.setName( options.attributesExportNames.value( attrIdx, field.name() ) );
3111  details.outputFields.append( field );
3112  }
3113  else
3114  {
3115  QgsDebugMsg( QStringLiteral( "No such source field with index '%1' available." ).arg( attrIdx ) );
3116  }
3117  }
3118  }
3119 
3120  // not ideal - would be nice to avoid this happening in the preparation step if possible,
3121  // but currently requires access to the layer's minimumValue/maximumValue methods
3122  if ( details.providerType == QLatin1String( "spatialite" ) )
3123  {
3124  for ( int i = 0; i < details.outputFields.size(); i++ )
3125  {
3126  if ( details.outputFields.at( i ).type() == QVariant::LongLong )
3127  {
3128  QVariant min;
3129  QVariant max;
3130  layer->minimumAndMaximumValue( i, min, max );
3131  if ( std::max( std::llabs( min.toLongLong() ), std::llabs( max.toLongLong() ) ) < std::numeric_limits<int>::max() )
3132  {
3133  details.outputFields[i].setType( QVariant::Int );
3134  }
3135  }
3136  }
3137  }
3138 
3139 
3140  //add possible attributes needed by renderer
3141  addRendererAttributes( details.renderer.get(), details.renderContext, details.sourceFields, details.attributes );
3142 
3143  QgsFeatureRequest req;
3144  req.setSubsetOfAttributes( details.attributes );
3145  if ( options.onlySelectedFeatures )
3146  req.setFilterFids( details.selectedFeatureIds );
3147 
3148  if ( !options.filterExtent.isNull() )
3149  {
3150  QgsRectangle filterRect = options.filterExtent;
3151  bool useFilterRect = true;
3152  if ( details.shallTransform )
3153  {
3154  try
3155  {
3156  // map filter rect back from destination CRS to layer CRS
3157  QgsCoordinateTransform extentTransform = options.ct;
3158  extentTransform.setBallparkTransformsAreAppropriate( true );
3159  filterRect = extentTransform.transformBoundingBox( filterRect, Qgis::TransformDirection::Reverse );
3160  }
3161  catch ( QgsCsException & )
3162  {
3163  useFilterRect = false;
3164  }
3165  }
3166  if ( useFilterRect )
3167  {
3168  req.setFilterRect( filterRect );
3169  }
3170  details.filterRectGeometry = QgsGeometry::fromRect( options.filterExtent );
3171  details.filterRectEngine.reset( QgsGeometry::createGeometryEngine( details.filterRectGeometry.constGet() ) );
3172  details.filterRectEngine->prepareGeometry();
3173  }
3174  details.sourceFeatureIterator = layer->getFeatures( req );
3175 
3176  return NoError;
3177 }
3178 
3179 QgsVectorFileWriter::WriterError QgsVectorFileWriter::writeAsVectorFormat( PreparedWriterDetails &details, const QString &fileName, const QgsVectorFileWriter::SaveVectorOptions &options, QString *newFilename, QString *errorMessage, QString *newLayer )
3180 {
3181  return writeAsVectorFormatV2( details, fileName, QgsCoordinateTransformContext(), options, newFilename, newLayer, errorMessage );
3182 }
3183 
3184 QgsVectorFileWriter::WriterError QgsVectorFileWriter::writeAsVectorFormatV2( PreparedWriterDetails &details, const QString &fileName, const QgsCoordinateTransformContext &transformContext, const QgsVectorFileWriter::SaveVectorOptions &options, QString *newFilename, QString *newLayer, QString *errorMessage )
3185 {
3186  QgsWkbTypes::Type destWkbType = details.destWkbType;
3187 
3188  int lastProgressReport = 0;
3189  long long total = details.featureCount;
3190 
3191  // Special rules for OGR layers
3192  if ( details.providerType == QLatin1String( "ogr" ) && !details.dataSourceUri.isEmpty() )
3193  {
3194  QString srcFileName( details.providerUriParams.value( QStringLiteral( "path" ) ).toString() );
3195  if ( QFile::exists( srcFileName ) && QFileInfo( fileName ).canonicalFilePath() == QFileInfo( srcFileName ).canonicalFilePath() )
3196  {
3197  // Check the layer name too if it's a GPKG/SpatiaLite/SQLite OGR driver (pay attention: camel case in layerName)
3198  QgsDataSourceUri uri( details.dataSourceUri );
3199  if ( !( ( options.driverName == QLatin1String( "GPKG" ) ||
3200  options.driverName == QLatin1String( "SpatiaLite" ) ||
3201  options.driverName == QLatin1String( "SQLite" ) ) &&
3202  options.layerName != details.providerUriParams.value( QStringLiteral( "layerName" ) ) ) )
3203  {
3204  if ( errorMessage )
3205  *errorMessage = QObject::tr( "Cannot overwrite a OGR layer in place" );
3206  return ErrCreateDataSource;
3207  }
3208  }
3209 
3210  // Shapefiles might contain multi types although wkbType() only reports singles
3211  if ( details.storageType == QLatin1String( "ESRI Shapefile" ) && !QgsWkbTypes::isMultiType( destWkbType ) )
3212  {
3213  QgsFeatureIterator fit = details.geometryTypeScanIterator;
3214  QgsFeature fet;
3215  long scanned = 0;
3216  while ( fit.nextFeature( fet ) )
3217  {
3218  if ( options.feedback && options.feedback->isCanceled() )
3219  {
3220  return Canceled;
3221  }
3222  if ( options.feedback )
3223  {
3224  //dedicate first 5% of progress bar to this scan
3225  int newProgress = static_cast<int>( ( 5.0 * scanned ) / total );
3226  if ( newProgress != lastProgressReport )
3227  {
3228  lastProgressReport = newProgress;
3229  options.feedback->setProgress( lastProgressReport );
3230  }
3231  }
3232 
3233  if ( fet.hasGeometry() && QgsWkbTypes::isMultiType( fet.geometry().wkbType() ) )
3234  {
3235  destWkbType = QgsWkbTypes::multiType( destWkbType );
3236  break;
3237  }
3238  scanned++;
3239  }
3240  }
3241  }
3242 
3243  QString tempNewFilename;
3244  QString tempNewLayer;
3245 
3246  std::unique_ptr< QgsVectorFileWriter > writer( create( fileName, details.outputFields, destWkbType, details.outputCrs, transformContext, options, QgsFeatureSink::SinkFlags(), &tempNewFilename, &tempNewLayer ) );
3247  writer->setSymbologyScale( options.symbologyScale );
3248 
3249  if ( newFilename )
3250  *newFilename = tempNewFilename;
3251 
3252  if ( newLayer )
3253  *newLayer = tempNewLayer;
3254 
3255  if ( newFilename )
3256  {
3257  QgsDebugMsgLevel( "newFilename = " + *newFilename, 2 );
3258  }
3259 
3260  // check whether file creation was successful
3261  WriterError err = writer->hasError();
3262  if ( err != NoError )
3263  {
3264  if ( errorMessage )
3265  *errorMessage = writer->errorMessage();
3266  return err;
3267  }
3268 
3269  if ( errorMessage )
3270  {
3271  errorMessage->clear();
3272  }
3273 
3274  QgsFeature fet;
3275 
3276  //create symbol table if needed
3277  if ( writer->symbologyExport() != NoSymbology )
3278  {
3279  //writer->createSymbolLayerTable( layer, writer->mDS );
3280  }
3281 
3282  if ( writer->symbologyExport() == SymbolLayerSymbology )
3283  {
3284  QgsFeatureRenderer *r = details.renderer.get();
3286  && r->usingSymbolLevels() )
3287  {
3288  QgsVectorFileWriter::WriterError error = writer->exportFeaturesSymbolLevels( details, details.sourceFeatureIterator, options.ct, errorMessage );
3289  return ( error == NoError ) ? NoError : ErrFeatureWriteFailed;
3290  }
3291  }
3292 
3293  int n = 0, errors = 0;
3294 
3295  //unit type
3296  QgsUnitTypes::DistanceUnit mapUnits = details.sourceCrs.mapUnits();
3297  if ( options.ct.isValid() )
3298  {
3299  mapUnits = options.ct.destinationCrs().mapUnits();
3300  }
3301 
3302  writer->startRender( details.renderer.get(), details.sourceFields );
3303 
3304  writer->resetMap( details.attributes );
3305  // Reset mFields to layer fields, and not just exported fields
3306  writer->mFields = details.sourceFields;
3307 
3308  // write all features
3309  long saved = 0;
3310  int initialProgress = lastProgressReport;
3311  while ( details.sourceFeatureIterator.nextFeature( fet ) )
3312  {
3313  if ( options.feedback && options.feedback->isCanceled() )
3314  {
3315  return Canceled;
3316  }
3317 
3318  saved++;
3319  if ( options.feedback )
3320  {
3321  //avoid spamming progress reports
3322  int newProgress = static_cast<int>( initialProgress + ( ( 100.0 - initialProgress ) * saved ) / total );
3323  if ( newProgress < 100 && newProgress != lastProgressReport )
3324  {
3325  lastProgressReport = newProgress;
3326  options.feedback->setProgress( lastProgressReport );
3327  }
3328  }
3329 
3330  if ( details.shallTransform )
3331  {
3332  try
3333  {
3334  if ( fet.hasGeometry() )
3335  {
3336  QgsGeometry g = fet.geometry();
3337  g.transform( options.ct );
3338  fet.setGeometry( g );
3339  }
3340  }
3341  catch ( QgsCsException &e )
3342  {
3343  QString msg = QObject::tr( "Failed to transform a point while drawing a feature with ID '%1'. Writing stopped. (Exception: %2)" )
3344  .arg( fet.id() ).arg( e.what() );
3345  QgsLogger::warning( msg );
3346  if ( errorMessage )
3347  *errorMessage = msg;
3348 
3349  return ErrProjection;
3350  }
3351  }
3352 
3353  if ( fet.hasGeometry() && details.filterRectEngine && !details.filterRectEngine->intersects( fet.geometry().constGet() ) )
3354  continue;
3355 
3356  if ( details.attributes.empty() && options.skipAttributeCreation )
3357  {
3358  fet.initAttributes( 0 );
3359  }
3360 
3361  if ( !writer->addFeatureWithStyle( fet, writer->mRenderer.get(), mapUnits ) )
3362  {
3363  WriterError err = writer->hasError();
3364  if ( err != NoError && errorMessage )
3365  {
3366  if ( errorMessage->isEmpty() )
3367  {
3368  *errorMessage = QObject::tr( "Feature write errors:" );
3369  }
3370  *errorMessage += '\n' + writer->errorMessage();
3371  }
3372  errors++;
3373 
3374  if ( errors > 1000 )
3375  {
3376  if ( errorMessage )
3377  {
3378  *errorMessage += QObject::tr( "Stopping after %n error(s)", nullptr, errors );
3379  }
3380 
3381  n = -1;
3382  break;
3383  }
3384  }
3385  n++;
3386  }
3387 
3388  writer->stopRender();
3389 
3390  if ( errors > 0 && errorMessage && n > 0 )
3391  {
3392  *errorMessage += QObject::tr( "\nOnly %1 of %2 features written." ).arg( n - errors ).arg( n );
3393  }
3394 
3395  writer.reset();
3396 
3397  bool metadataFailure = false;
3398  if ( options.saveMetadata )
3399  {
3400  QString uri = QgsProviderRegistry::instance()->encodeUri( QStringLiteral( "ogr" ), QVariantMap
3401  {
3402  {QStringLiteral( "path" ), tempNewFilename },
3403  {QStringLiteral( "layerName" ), tempNewLayer }
3404  } );
3405 
3406  try
3407  {
3408  QString error;
3409  if ( !QgsProviderRegistry::instance()->saveLayerMetadata( QStringLiteral( "ogr" ), uri, options.layerMetadata, error ) )
3410  {
3411  if ( errorMessage )
3412  {
3413  if ( !errorMessage->isEmpty() )
3414  *errorMessage += '\n';
3415  *errorMessage += error;
3416  }
3417  metadataFailure = true;
3418  }
3419  }
3420  catch ( QgsNotSupportedException &e )
3421  {
3422  if ( errorMessage )
3423  {
3424  if ( !errorMessage->isEmpty() )
3425  *errorMessage += '\n';
3426  *errorMessage += e.what();
3427  }
3428  metadataFailure = true;
3429  }
3430  }
3431 
3432  return errors == 0 ? ( !metadataFailure ? NoError : ErrSavingMetadata ) : ErrFeatureWriteFailed;
3433 }
3434 
3436  const QString &fileName,
3437  const SaveVectorOptions &options,
3438  QString *newFilename,
3439  QString *errorMessage,
3440  QString *newLayer )
3441 {
3442  QgsVectorFileWriter::PreparedWriterDetails details;
3443  WriterError err = prepareWriteAsVectorFormat( layer, options, details );
3444  if ( err != NoError )
3445  return err;
3446 
3447  return writeAsVectorFormatV2( details, fileName, layer->transformContext(), options, newFilename, newLayer, errorMessage );
3448 }
3449 
3451  const QString &fileName,
3452  const QgsCoordinateTransformContext &transformContext,
3453  const SaveVectorOptions &options,
3454  QString *newFilename,
3455  QString *newLayer,
3456  QString *errorMessage )
3457 {
3458  QgsVectorFileWriter::PreparedWriterDetails details;
3459  WriterError err = prepareWriteAsVectorFormat( layer, options, details );
3460  if ( err != NoError )
3461  return err;
3462 
3463  return writeAsVectorFormatV2( details, fileName, transformContext, options, errorMessage, newFilename, newLayer );
3464 }
3465 
3466 QgsVectorFileWriter::WriterError QgsVectorFileWriter::writeAsVectorFormatV3( QgsVectorLayer *layer, const QString &fileName, const QgsCoordinateTransformContext &transformContext, const QgsVectorFileWriter::SaveVectorOptions &options, QString *errorMessage, QString *newFilename, QString *newLayer )
3467 {
3468  QgsVectorFileWriter::PreparedWriterDetails details;
3469  WriterError err = prepareWriteAsVectorFormat( layer, options, details );
3470  if ( err != NoError )
3471  return err;
3472 
3473  return writeAsVectorFormatV2( details, fileName, transformContext, options, newFilename, newLayer, errorMessage );
3474 }
3475 
3476 bool QgsVectorFileWriter::deleteShapeFile( const QString &fileName )
3477 {
3478  QFileInfo fi( fileName );
3479  QDir dir = fi.dir();
3480 
3481  QStringList filter;
3482  for ( const char *suffix : { ".shp", ".shx", ".dbf", ".prj", ".qix", ".qpj", ".cpg", ".sbn", ".sbx", ".idm", ".ind" } )
3483  {
3484  filter << fi.completeBaseName() + suffix;
3485  }
3486 
3487  bool ok = true;
3488  const auto constEntryList = dir.entryList( filter );
3489  for ( const QString &file : constEntryList )
3490  {
3491  QFile f( dir.canonicalPath() + '/' + file );
3492  if ( !f.remove() )
3493  {
3494  QgsDebugMsg( QStringLiteral( "Removing file %1 failed: %2" ).arg( file, f.errorString() ) );
3495  ok = false;
3496  }
3497  }
3498 
3499  return ok;
3500 }
3501 
3503 {
3504  mSymbologyScale = d;
3505  mRenderContext.setRendererScale( mSymbologyScale );
3506 }
3507 
3508 QList< QgsVectorFileWriter::FilterFormatDetails > QgsVectorFileWriter::supportedFiltersAndFormats( const VectorFormatOptions options )
3509 {
3510  static QReadWriteLock sFilterLock;
3511  static QMap< VectorFormatOptions, QList< QgsVectorFileWriter::FilterFormatDetails > > sFilters;
3512 
3513  QgsReadWriteLocker locker( sFilterLock, QgsReadWriteLocker::Read );
3514 
3515  const auto it = sFilters.constFind( options );
3516  if ( it != sFilters.constEnd() )
3517  return it.value();
3518 
3520  QList< QgsVectorFileWriter::FilterFormatDetails > results;
3521 
3523  int const drvCount = OGRGetDriverCount();
3524 
3525  for ( int i = 0; i < drvCount; ++i )
3526  {
3527  OGRSFDriverH drv = OGRGetDriver( i );
3528  if ( drv )
3529  {
3530  QString drvName = OGR_Dr_GetName( drv );
3531 
3532  GDALDriverH gdalDriver = GDALGetDriverByName( drvName.toLocal8Bit().constData() );
3533  char **metadata = nullptr;
3534  if ( gdalDriver )
3535  {
3536  metadata = GDALGetMetadata( gdalDriver, nullptr );
3537  }
3538 
3539  bool nonSpatialFormat = CSLFetchBoolean( metadata, GDAL_DCAP_NONSPATIAL, false );
3540 
3541  if ( OGR_Dr_TestCapability( drv, "CreateDataSource" ) != 0 )
3542  {
3543  if ( options & SkipNonSpatialFormats )
3544  {
3545  // skip non-spatial formats
3546  if ( nonSpatialFormat )
3547  continue;
3548  }
3549 
3550  QString filterString = filterForDriver( drvName );
3551  if ( filterString.isEmpty() )
3552  continue;
3553 
3554  MetaData metadata;
3555  QStringList globs;
3556  if ( driverMetadata( drvName, metadata ) && !metadata.glob.isEmpty() )
3557  {
3558  globs = metadata.glob.toLower().split( ' ' );
3559  }
3560 
3561  FilterFormatDetails details;
3562  details.driverName = drvName;
3563  details.filterString = filterString;
3564  details.globs = globs;
3565 
3566  results << details;
3567  }
3568  }
3569  }
3570 
3571  std::sort( results.begin(), results.end(), [options]( const FilterFormatDetails & a, const FilterFormatDetails & b ) -> bool
3572  {
3573  if ( options & SortRecommended )
3574  {
3575  if ( a.driverName == QLatin1String( "GPKG" ) )
3576  return true; // Make https://twitter.com/shapefiIe a sad little fellow
3577  else if ( b.driverName == QLatin1String( "GPKG" ) )
3578  return false;
3579  else if ( a.driverName == QLatin1String( "ESRI Shapefile" ) )
3580  return true;
3581  else if ( b.driverName == QLatin1String( "ESRI Shapefile" ) )
3582  return false;
3583  }
3584 
3585  return a.filterString.toLower().localeAwareCompare( b.filterString.toLower() ) < 0;
3586  } );
3587 
3588  sFilters.insert( options, results );
3589  return results;
3590 }
3591 
3592 QStringList QgsVectorFileWriter::supportedFormatExtensions( const VectorFormatOptions options )
3593 {
3594  const auto formats = supportedFiltersAndFormats( options );
3595  QSet< QString > extensions;
3596 
3597  const QRegularExpression rx( QStringLiteral( "\\*\\.(.*)$" ) );
3598 
3599  for ( const FilterFormatDetails &format : formats )
3600  {
3601  for ( const QString &glob : format.globs )
3602  {
3603  const QRegularExpressionMatch match = rx.match( glob );
3604  if ( !match.hasMatch() )
3605  continue;
3606 
3607  const QString matched = match.captured( 1 );
3608  extensions.insert( matched );
3609  }
3610  }
3611 
3612  QStringList extensionList = qgis::setToList( extensions );
3613 
3614  std::sort( extensionList.begin(), extensionList.end(), [options]( const QString & a, const QString & b ) -> bool
3615  {
3616  if ( options & SortRecommended )
3617  {
3618  if ( a == QLatin1String( "gpkg" ) )
3619  return true; // Make https://twitter.com/shapefiIe a sad little fellow
3620  else if ( b == QLatin1String( "gpkg" ) )
3621  return false;
3622  else if ( a == QLatin1String( "shp" ) )
3623  return true;
3624  else if ( b == QLatin1String( "shp" ) )
3625  return false;
3626  }
3627 
3628  return a.toLower().localeAwareCompare( b.toLower() ) < 0;
3629  } );
3630 
3631  return extensionList;
3632 }
3633 
3634 QList< QgsVectorFileWriter::DriverDetails > QgsVectorFileWriter::ogrDriverList( const VectorFormatOptions options )
3635 {
3636  QList< QgsVectorFileWriter::DriverDetails > results;
3637 
3639  const int drvCount = OGRGetDriverCount();
3640 
3641  QStringList writableDrivers;
3642  for ( int i = 0; i < drvCount; ++i )
3643  {
3644  OGRSFDriverH drv = OGRGetDriver( i );
3645  if ( drv )
3646  {
3647  QString drvName = OGR_Dr_GetName( drv );
3648 
3649  if ( options & SkipNonSpatialFormats )
3650  {
3651  // skip non-spatial formats
3652  // TODO - use GDAL metadata to determine this, when support exists in GDAL
3653  if ( drvName == QLatin1String( "ODS" ) || drvName == QLatin1String( "XLSX" ) || drvName == QLatin1String( "XLS" ) )
3654  continue;
3655  }
3656 
3657  if ( drvName == QLatin1String( "ESRI Shapefile" ) )
3658  {
3659  writableDrivers << QStringLiteral( "DBF file" );
3660  }
3661  if ( OGR_Dr_TestCapability( drv, "CreateDataSource" ) != 0 )
3662  {
3663  // Add separate format for Mapinfo MIF (MITAB is OGR default)
3664  if ( drvName == QLatin1String( "MapInfo File" ) )
3665  {
3666  writableDrivers << QStringLiteral( "MapInfo MIF" );
3667  }
3668  else if ( drvName == QLatin1String( "SQLite" ) )
3669  {
3670  // Unfortunately it seems that there is no simple way to detect if
3671  // OGR SQLite driver is compiled with SpatiaLite support.
3672  // We have HAVE_SPATIALITE in QGIS, but that may differ from OGR
3673  // http://lists.osgeo.org/pipermail/gdal-dev/2012-November/034580.html
3674  // -> test if creation fails
3675  QString option = QStringLiteral( "SPATIALITE=YES" );
3676  char *options[2] = { CPLStrdup( option.toLocal8Bit().constData() ), nullptr };
3677  OGRSFDriverH poDriver;
3679  poDriver = OGRGetDriverByName( drvName.toLocal8Bit().constData() );
3680  if ( poDriver )
3681  {
3682  gdal::ogr_datasource_unique_ptr ds( OGR_Dr_CreateDataSource( poDriver, QStringLiteral( "/vsimem/spatialitetest.sqlite" ).toUtf8().constData(), options ) );
3683  if ( ds )
3684  {
3685  writableDrivers << QStringLiteral( "SpatiaLite" );
3686  OGR_Dr_DeleteDataSource( poDriver, QStringLiteral( "/vsimem/spatialitetest.sqlite" ).toUtf8().constData() );
3687  }
3688  }
3689  CPLFree( options[0] );
3690  }
3691  writableDrivers << drvName;
3692  }
3693  }
3694  }
3695 
3696  results.reserve( writableDrivers.count() );
3697  for ( const QString &drvName : std::as_const( writableDrivers ) )
3698  {
3699  MetaData metadata;
3700  if ( driverMetadata( drvName, metadata ) && !metadata.trLongName.isEmpty() )
3701  {
3702  DriverDetails details;
3703  details.driverName = drvName;
3704  details.longName = metadata.trLongName;
3705  results << details;
3706  }
3707  }
3708 
3709  std::sort( results.begin(), results.end(), [options]( const DriverDetails & a, const DriverDetails & b ) -> bool
3710  {
3711  if ( options & SortRecommended )
3712  {
3713  if ( a.driverName == QLatin1String( "GPKG" ) )
3714  return true; // Make https://twitter.com/shapefiIe a sad little fellow
3715  else if ( b.driverName == QLatin1String( "GPKG" ) )
3716  return false;
3717  else if ( a.driverName == QLatin1String( "ESRI Shapefile" ) )
3718  return true;
3719  else if ( b.driverName == QLatin1String( "ESRI Shapefile" ) )
3720  return false;
3721  }
3722 
3723  return a.longName.toLower().localeAwareCompare( b.longName.toLower() ) < 0;
3724  } );
3725  return results;
3726 }
3727 
3728 QString QgsVectorFileWriter::driverForExtension( const QString &extension )
3729 {
3730  QString ext = extension.trimmed();
3731  if ( ext.isEmpty() )
3732  return QString();
3733 
3734  if ( ext.startsWith( '.' ) )
3735  ext.remove( 0, 1 );
3736 
3737  GDALAllRegister();
3738  int const drvCount = GDALGetDriverCount();
3739 
3740  for ( int i = 0; i < drvCount; ++i )
3741  {
3742  GDALDriverH drv = GDALGetDriver( i );
3743  if ( drv )
3744  {
3745  char **driverMetadata = GDALGetMetadata( drv, nullptr );
3746  if ( CSLFetchBoolean( driverMetadata, GDAL_DCAP_CREATE, false ) && CSLFetchBoolean( driverMetadata, GDAL_DCAP_VECTOR, false ) )
3747  {
3748  QString drvName = GDALGetDriverShortName( drv );
3749  QStringList driverExtensions = QString( GDALGetMetadataItem( drv, GDAL_DMD_EXTENSIONS, nullptr ) ).split( ' ' );
3750 
3751  const auto constDriverExtensions = driverExtensions;
3752  for ( const QString &driver : constDriverExtensions )
3753  {
3754  if ( driver.compare( ext, Qt::CaseInsensitive ) == 0 )
3755  return drvName;
3756  }
3757  }
3758  }
3759  }
3760  return QString();
3761 }
3762 
3763 QString QgsVectorFileWriter::fileFilterString( const VectorFormatOptions options )
3764 {
3765  QString filterString;
3766  const auto driverFormats = supportedFiltersAndFormats( options );
3767  for ( const FilterFormatDetails &details : driverFormats )
3768  {
3769  if ( !filterString.isEmpty() )
3770  filterString += QLatin1String( ";;" );
3771 
3772  filterString += details.filterString;
3773  }
3774  return filterString;
3775 }
3776 
3777 QString QgsVectorFileWriter::filterForDriver( const QString &driverName )
3778 {
3779  MetaData metadata;
3780  if ( !driverMetadata( driverName, metadata ) || metadata.trLongName.isEmpty() || metadata.glob.isEmpty() )
3781  return QString();
3782 
3783  return QStringLiteral( "%1 (%2 %3)" ).arg( metadata.trLongName,
3784  metadata.glob.toLower(),
3785  metadata.glob.toUpper() );
3786 }
3787 
3789 {
3790  if ( codecName == QLatin1String( "System" ) )
3791  return QStringLiteral( "LDID/0" );
3792 
3793  const QRegularExpression re( QRegularExpression::anchoredPattern( QString( "(CP|windows-|ISO[ -])(.+)" ) ), QRegularExpression::CaseInsensitiveOption );
3794  const QRegularExpressionMatch match = re.match( codecName );
3795  if ( match.hasMatch() )
3796  {
3797  QString c = match.captured( 2 ).remove( '-' );
3798  bool isNumber;
3799  ( void ) c.toInt( &isNumber );
3800  if ( isNumber )
3801  return c;
3802  }
3803  return codecName;
3804 }
3805 
3806 void QgsVectorFileWriter::createSymbolLayerTable( QgsVectorLayer *vl, const QgsCoordinateTransform &ct, OGRDataSourceH ds )
3807 {
3808  if ( !vl || !ds )
3809  {
3810  return;
3811  }
3812 
3813  QgsFeatureRenderer *renderer = vl->renderer();
3814  if ( !renderer )
3815  {
3816  return;
3817  }
3818 
3819  //unit type
3820  QgsUnitTypes::DistanceUnit mapUnits = vl->crs().mapUnits();
3821  if ( ct.isValid() )
3822  {
3823  mapUnits = ct.destinationCrs().mapUnits();
3824  }
3825 
3826  mSymbolLayerTable.clear();
3827  OGRStyleTableH ogrStyleTable = OGR_STBL_Create();
3828  OGRStyleMgrH styleManager = OGR_SM_Create( ogrStyleTable );
3829 
3830  //get symbols
3831  int nTotalLevels = 0;
3832  QgsSymbolList symbolList = renderer->symbols( mRenderContext );
3833  QgsSymbolList::iterator symbolIt = symbolList.begin();
3834  for ( ; symbolIt != symbolList.end(); ++symbolIt )
3835  {
3836  double mmsf = mmScaleFactor( mSymbologyScale, ( *symbolIt )->outputUnit(), mapUnits );
3837  double musf = mapUnitScaleFactor( mSymbologyScale, ( *symbolIt )->outputUnit(), mapUnits );
3838 
3839  int nLevels = ( *symbolIt )->symbolLayerCount();
3840  for ( int i = 0; i < nLevels; ++i )
3841  {
3842  mSymbolLayerTable.insert( ( *symbolIt )->symbolLayer( i ), QString::number( nTotalLevels ) );
3843  OGR_SM_AddStyle( styleManager, QString::number( nTotalLevels ).toLocal8Bit(),
3844  ( *symbolIt )->symbolLayer( i )->ogrFeatureStyle( mmsf, musf ).toLocal8Bit() );
3845  ++nTotalLevels;
3846  }
3847  }
3848  OGR_DS_SetStyleTableDirectly( ds, ogrStyleTable );
3849 }
3850 
3851 QgsVectorFileWriter::WriterError QgsVectorFileWriter::exportFeaturesSymbolLevels( const PreparedWriterDetails &details, QgsFeatureIterator &fit,
3852  const QgsCoordinateTransform &ct, QString *errorMessage )
3853 {
3854  if ( !details.renderer )
3855  return ErrInvalidLayer;
3856 
3857  mRenderContext.expressionContext() = details.expressionContext;
3858 
3859  QHash< QgsSymbol *, QList<QgsFeature> > features;
3860 
3861  //unit type
3862  QgsUnitTypes::DistanceUnit mapUnits = details.sourceCrs.mapUnits();
3863  if ( ct.isValid() )
3864  {
3865  mapUnits = ct.destinationCrs().mapUnits();
3866  }
3867 
3868  startRender( details.renderer.get(), details.sourceFields );
3869 
3870  //fetch features
3871  QgsFeature fet;
3872  QgsSymbol *featureSymbol = nullptr;
3873  while ( fit.nextFeature( fet ) )
3874  {
3875  if ( ct.isValid() )
3876  {
3877  try
3878  {
3879  if ( fet.hasGeometry() )
3880  {
3881  QgsGeometry g = fet.geometry();
3882  g.transform( ct );
3883  fet.setGeometry( g );
3884  }
3885  }
3886  catch ( QgsCsException &e )
3887  {
3888  QString msg = QObject::tr( "Failed to transform, writing stopped. (Exception: %1)" )
3889  .arg( e.what() );
3890  QgsLogger::warning( msg );
3891  if ( errorMessage )
3892  *errorMessage = msg;
3893 
3894  return ErrProjection;
3895  }
3896  }
3897  mRenderContext.expressionContext().setFeature( fet );
3898 
3899  featureSymbol = mRenderer->symbolForFeature( fet, mRenderContext );
3900  if ( !featureSymbol )
3901  {
3902  continue;
3903  }
3904 
3905  QHash< QgsSymbol *, QList<QgsFeature> >::iterator it = features.find( featureSymbol );
3906  if ( it == features.end() )
3907  {
3908  it = features.insert( featureSymbol, QList<QgsFeature>() );
3909  }
3910  it.value().append( fet );
3911  }
3912 
3913  //find out order
3914  QgsSymbolLevelOrder levels;
3915  QgsSymbolList symbols = mRenderer->symbols( mRenderContext );
3916  for ( int i = 0; i < symbols.count(); i++ )
3917  {
3918  QgsSymbol *sym = symbols[i];
3919  for ( int j = 0; j < sym->symbolLayerCount(); j++ )
3920  {
3921  int level = sym->symbolLayer( j )->renderingPass();
3922  if ( level < 0 || level >= 1000 ) // ignore invalid levels
3923  continue;
3924  QgsSymbolLevelItem item( sym, j );
3925  while ( level >= levels.count() ) // append new empty levels
3926  levels.append( QgsSymbolLevel() );
3927  levels[level].append( item );
3928  }
3929  }
3930 
3931  int nErrors = 0;
3932  int nTotalFeatures = 0;
3933 
3934  //export symbol layers and symbology
3935  for ( int l = 0; l < levels.count(); l++ )
3936  {
3937  QgsSymbolLevel &level = levels[l];
3938  for ( int i = 0; i < level.count(); i++ )
3939  {
3940  QgsSymbolLevelItem &item = level[i];
3941  QHash< QgsSymbol *, QList<QgsFeature> >::iterator levelIt = features.find( item.symbol() );
3942  if ( levelIt == features.end() )
3943  {
3944  ++nErrors;
3945  continue;
3946  }
3947 
3948  double mmsf = mmScaleFactor( mSymbologyScale, levelIt.key()->outputUnit(), mapUnits );
3949  double musf = mapUnitScaleFactor( mSymbologyScale, levelIt.key()->outputUnit(), mapUnits );
3950 
3951  int llayer = item.layer();
3952  QList<QgsFeature> &featureList = levelIt.value();
3953  QList<QgsFeature>::iterator featureIt = featureList.begin();
3954  for ( ; featureIt != featureList.end(); ++featureIt )
3955  {
3956  ++nTotalFeatures;
3957  gdal::ogr_feature_unique_ptr ogrFeature = createFeature( *featureIt );
3958  if ( !ogrFeature )
3959  {
3960  ++nErrors;
3961  continue;
3962  }
3963 
3964  QString styleString = levelIt.key()->symbolLayer( llayer )->ogrFeatureStyle( mmsf, musf );
3965  if ( !styleString.isEmpty() )
3966  {
3967  OGR_F_SetStyleString( ogrFeature.get(), styleString.toLocal8Bit().constData() );
3968  if ( !writeFeature( mLayer, ogrFeature.get() ) )
3969  {
3970  ++nErrors;
3971  }
3972  }
3973  }
3974  }
3975  }
3976 
3977  stopRender();
3978 
3979  if ( nErrors > 0 && errorMessage )
3980  {
3981  *errorMessage += QObject::tr( "\nOnly %1 of %2 features written." ).arg( nTotalFeatures - nErrors ).arg( nTotalFeatures );
3982  }
3983 
3985 }
3986 
3987 double QgsVectorFileWriter::mmScaleFactor( double scale, QgsUnitTypes::RenderUnit symbolUnits, QgsUnitTypes::DistanceUnit mapUnits )
3988 {
3989  if ( symbolUnits == QgsUnitTypes::RenderMillimeters )
3990  {
3991  return 1.0;
3992  }
3993  else
3994  {
3995  //conversion factor map units -> mm
3996  if ( mapUnits == QgsUnitTypes::DistanceMeters )
3997  {
3998  return 1000 / scale;
3999  }
4000 
4001  }
4002  return 1.0; //todo: map units
4003 }
4004 
4005 double QgsVectorFileWriter::mapUnitScaleFactor( double scale, QgsUnitTypes::RenderUnit symbolUnits, QgsUnitTypes::DistanceUnit mapUnits )
4006 {
4007  if ( symbolUnits == QgsUnitTypes::RenderMapUnits )
4008  {
4009  return 1.0;
4010  }
4011  else
4012  {
4013  if ( symbolUnits == QgsUnitTypes::RenderMillimeters && mapUnits == QgsUnitTypes::DistanceMeters )
4014  {
4015  return scale / 1000;
4016  }
4017  }
4018  return 1.0;
4019 }
4020 
4021 void QgsVectorFileWriter::startRender( QgsFeatureRenderer *sourceRenderer, const QgsFields &fields )
4022 {
4023  mRenderer = createSymbologyRenderer( sourceRenderer );
4024  if ( !mRenderer )
4025  {
4026  return;
4027  }
4028 
4029  mRenderer->startRender( mRenderContext, fields );
4030 }
4031 
4032 void QgsVectorFileWriter::stopRender()
4033 {
4034  if ( !mRenderer )
4035  {
4036  return;
4037  }
4038 
4039  mRenderer->stopRender( mRenderContext );
4040 }
4041 
4042 std::unique_ptr<QgsFeatureRenderer> QgsVectorFileWriter::createSymbologyRenderer( QgsFeatureRenderer *sourceRenderer ) const
4043 {
4044  if ( mSymbologyExport == NoSymbology )
4045  {
4046  return nullptr;
4047  }
4048  if ( !sourceRenderer )
4049  {
4050  return nullptr;
4051  }
4052 
4053  return std::unique_ptr< QgsFeatureRenderer >( sourceRenderer->clone() );
4054 }
4055 
4056 void QgsVectorFileWriter::addRendererAttributes( QgsFeatureRenderer *renderer, QgsRenderContext &context, const QgsFields &fields, QgsAttributeList &attList )
4057 {
4058  if ( renderer )
4059  {
4060  const QSet<QString> rendererAttributes = renderer->usedAttributes( context );
4061  for ( const QString &attr : rendererAttributes )
4062  {
4063  int index = fields.lookupField( attr );
4064  if ( index != -1 )
4065  {
4066  attList.append( index );
4067  }
4068  }
4069  }
4070 }
4071 
4072 QStringList QgsVectorFileWriter::concatenateOptions( const QMap<QString, QgsVectorFileWriter::Option *> &options )
4073 {
4074  QStringList list;
4075  QMap<QString, QgsVectorFileWriter::Option *>::ConstIterator it;
4076 
4077  for ( it = options.constBegin(); it != options.constEnd(); ++it )
4078  {
4079  QgsVectorFileWriter::Option *option = it.value();
4080  switch ( option->type )
4081  {
4083  {
4084  QgsVectorFileWriter::IntOption *opt = dynamic_cast<QgsVectorFileWriter::IntOption *>( option );
4085  if ( opt )
4086  {
4087  list.append( QStringLiteral( "%1=%2" ).arg( it.key() ).arg( opt->defaultValue ) );
4088  }
4089  break;
4090  }
4091 
4093  {
4094  QgsVectorFileWriter::SetOption *opt = dynamic_cast<QgsVectorFileWriter::SetOption *>( option );
4095  if ( opt && !opt->defaultValue.isEmpty() )
4096  {
4097  list.append( QStringLiteral( "%1=%2" ).arg( it.key(), opt->defaultValue ) );
4098  }
4099  break;
4100  }
4101 
4103  {
4105  if ( opt && !opt->defaultValue.isNull() )
4106  {
4107  list.append( QStringLiteral( "%1=%2" ).arg( it.key(), opt->defaultValue ) );
4108  }
4109  break;
4110  }
4111 
4114  if ( opt )
4115  {
4116  list.append( QStringLiteral( "%1=%2" ).arg( it.key(), opt->mValue ) );
4117  }
4118  break;
4119  }
4120  }
4121 
4122  return list;
4123 }
4124 
4125 QgsVectorFileWriter::EditionCapabilities QgsVectorFileWriter::editionCapabilities( const QString &datasetName )
4126 {
4127  OGRSFDriverH hDriver = nullptr;
4128  gdal::ogr_datasource_unique_ptr hDS( OGROpen( datasetName.toUtf8().constData(), TRUE, &hDriver ) );
4129  if ( !hDS )
4130  return QgsVectorFileWriter::EditionCapabilities();
4131  QString drvName = OGR_Dr_GetName( hDriver );
4132  QgsVectorFileWriter::EditionCapabilities caps = QgsVectorFileWriter::EditionCapabilities();
4133  if ( OGR_DS_TestCapability( hDS.get(), ODsCCreateLayer ) )
4134  {
4135  // Shapefile driver returns True for a "foo.shp" dataset name,
4136  // creating "bar.shp" new layer, but this would be a bit confusing
4137  // for the user, so pretent that it does not support that
4138  if ( !( drvName == QLatin1String( "ESRI Shapefile" ) && QFile::exists( datasetName ) ) )
4139  caps |= CanAddNewLayer;
4140  }
4141  if ( OGR_DS_TestCapability( hDS.get(), ODsCDeleteLayer ) )
4142  {
4143  caps |= CanDeleteLayer;
4144  }
4145  int layer_count = OGR_DS_GetLayerCount( hDS.get() );
4146  if ( layer_count )
4147  {
4148  OGRLayerH hLayer = OGR_DS_GetLayer( hDS.get(), 0 );
4149  if ( hLayer )
4150  {
4151  if ( OGR_L_TestCapability( hLayer, OLCSequentialWrite ) )
4152  {
4153  caps |= CanAppendToExistingLayer;
4154  if ( OGR_L_TestCapability( hLayer, OLCCreateField ) )
4155  {
4157  }
4158  }
4159  }
4160  }
4161  return caps;
4162 }
4163 
4164 bool QgsVectorFileWriter::targetLayerExists( const QString &datasetName,
4165  const QString &layerNameIn )
4166 {
4167  OGRSFDriverH hDriver = nullptr;
4168  gdal::ogr_datasource_unique_ptr hDS( OGROpen( datasetName.toUtf8().constData(), TRUE, &hDriver ) );
4169  if ( !hDS )
4170  return false;
4171 
4172  QString layerName( layerNameIn );
4173  if ( layerName.isEmpty() )
4174  layerName = QFileInfo( datasetName ).baseName();
4175 
4176  return OGR_DS_GetLayerByName( hDS.get(), layerName.toUtf8().constData() );
4177 }
4178 
4179 
4180 bool QgsVectorFileWriter::areThereNewFieldsToCreate( const QString &datasetName,
4181  const QString &layerName,
4182  QgsVectorLayer *layer,
4183  const QgsAttributeList &attributes )
4184 {
4185  OGRSFDriverH hDriver = nullptr;
4186  gdal::ogr_datasource_unique_ptr hDS( OGROpen( datasetName.toUtf8().constData(), TRUE, &hDriver ) );
4187  if ( !hDS )
4188  return false;
4189  OGRLayerH hLayer = OGR_DS_GetLayerByName( hDS.get(), layerName.toUtf8().constData() );
4190  if ( !hLayer )
4191  {
4192  return false;
4193  }
4194  bool ret = false;
4195  OGRFeatureDefnH defn = OGR_L_GetLayerDefn( hLayer );
4196  const auto constAttributes = attributes;
4197  for ( int idx : constAttributes )
4198  {
4199  QgsField fld = layer->fields().at( idx );
4200  if ( OGR_FD_GetFieldIndex( defn, fld.name().toUtf8().constData() ) < 0 )
4201  {
4202  ret = true;
4203  break;
4204  }
4205  }
4206  return ret;
4207 }
virtual bool addZValue(double zValue=0)=0
Adds a z-dimension to the geometry, initialized to a preset value.
virtual bool dropMValue()=0
Drops any measure values which exist in the geometry.
virtual bool addMValue(double mValue=0)=0
Adds a measure to the geometry, initialized to a preset value.
virtual bool dropZValue()=0
Drops any z-dimensions which exist in the geometry.
@ FlagExportTrianglesAsPolygons
Triangles should be exported as polygon geometries.
static void registerOgrDrivers()
Register OGR drivers ensuring this only happens once.
This class represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
static Q_INVOKABLE QgsCoordinateReferenceSystem fromEpsgId(long epsg)
Creates a CRS from a given EPSG ID.
Q_GADGET QgsUnitTypes::DistanceUnit mapUnits
Contains information about the context in which a coordinate transform is executed.
Class for doing transforms between two map coordinate systems.
void setBallparkTransformsAreAppropriate(bool appropriate)
Sets whether approximate "ballpark" results are appropriate for this coordinate transform.
bool isValid() const
Returns true if the coordinate transform is valid, ie both the source and destination CRS have been s...
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system, which the transform will transform coordinates t...
QgsRectangle transformBoundingBox(const QgsRectangle &rectangle, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool handle180Crossover=false) const SIP_THROW(QgsCsException)
Transforms a rectangle from the source CRS to the destination CRS.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:66
virtual QString dataSourceUri(bool expandAuthConfig=false) const
Gets the data source specification.
Class for storing the component parts of a RDBMS data source URI (e.g.
QString what() const
Definition: qgsexception.h:48
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
virtual QgsFeatureRenderer * clone() const =0
Create a deep copy of this renderer.
virtual QgsSymbolList symbolsForFeature(const QgsFeature &feature, QgsRenderContext &context) const
Returns list of symbols used for rendering the feature.
virtual QgsSymbolList symbols(QgsRenderContext &context) const
Returns list of symbols used by the renderer.
bool usingSymbolLevels() const
Definition: qgsrenderer.h:292
virtual QgsFeatureRenderer::Capabilities capabilities()
Returns details about internals of this renderer.
Definition: qgsrenderer.h:283
@ SymbolLevels
Rendering with symbol levels (i.e. implements symbols(), symbolForFeature())
Definition: qgsrenderer.h:263
virtual QSet< QString > usedAttributes(const QgsRenderContext &context) const =0
Returns a list of attributes required by this renderer.
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFilterFids(const QgsFeatureIds &fids)
Sets the feature IDs that should be fetched.
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
QgsFeatureRequest & setNoAttributes()
Set that no attributes will be fetched.
QgsFeatureRequest & setFilterRect(const QgsRectangle &rectangle)
Sets the rectangle from which features will be taken.
@ RegeneratePrimaryKey
This flag indicates, that a primary key field cannot be guaranteed to be unique and the sink should i...
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
void initAttributes(int fieldCount)
Initialize this feature with the given number of fields.
Definition: qgsfeature.cpp:228
QgsGeometry geometry
Definition: qgsfeature.h:67
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:223
QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
Definition: qgsfeature.cpp:320
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
Definition: qgsfeature.cpp:163
Q_GADGET QgsFeatureId id
Definition: qgsfeature.h:64
bool isCanceled() const SIP_HOLDGIL
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:54
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition: qgsfeedback.h:63
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:51
QString typeName() const
Gets the field type.
Definition: qgsfield.cpp:139
QString name
Definition: qgsfield.h:60
int precision
Definition: qgsfield.h:57
int length
Definition: qgsfield.h:56
bool convertCompatible(QVariant &v, QString *errorMessage=nullptr) const
Converts the provided variant to a compatible format.
Definition: qgsfield.cpp:402
void setName(const QString &name)
Set the field name.
Definition: qgsfield.cpp:175
QVariant::Type type
Definition: qgsfield.h:58
QVariant::Type subType() const
If the field is a collection, gets its element's type.
Definition: qgsfield.cpp:134
QString alias
Definition: qgsfield.h:61
Container of fields for a vector layer.
Definition: qgsfields.h:45
int count() const
Returns number of items.
Definition: qgsfields.cpp:133
int size() const
Returns number of items.
Definition: qgsfields.cpp:138
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Definition: qgsfields.cpp:163
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
Definition: qgsfields.cpp:349
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:125
const QgsAbstractGeometry * constGet() const SIP_HOLDGIL
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
QgsWkbTypes::Type wkbType() const SIP_HOLDGIL
Returns type of the geometry as a WKB type (point / linestring / polygon etc.)
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false) SIP_THROW(QgsCsException)
Transforms this geometry as described by the coordinate transform ct.
QgsAbstractGeometry * get()
Returns a modifiable (non-const) reference to the underlying abstract geometry primitive.
static QgsGeometry fromRect(const QgsRectangle &rect) SIP_HOLDGIL
Creates a new geometry from a QgsRectangle.
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry)
Creates and returns a new geometry engine representing the specified geometry.
bool convertToMultiType()
Converts single type geometry into multitype geometry e.g.
QByteArray asWkb(QgsAbstractGeometry::WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const
Export the geometry to WKB.
static void warning(const QString &msg)
Goes to qWarning.
Definition: qgslogger.cpp:122
QString providerType() const
Returns the provider type (provider key) for this layer.
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:79
QgsCoordinateTransformContext transformContext() const
Returns the layer data provider coordinate transform context or a default transform context if the la...
bool isValid
Definition: qgsmaplayer.h:81
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
Custom exception class which is raised when an operation is not supported.
Definition: qgsexception.h:118
static OGRSpatialReferenceH crsToOGRSpatialReference(const QgsCoordinateReferenceSystem &crs)
Returns a OGRSpatialReferenceH corresponding to the specified crs object.
QVariantMap decodeUri(const QString &providerKey, const QString &uri)
Breaks a provider data source URI into its component paths (e.g.
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
QString encodeUri(const QString &providerKey, const QVariantMap &parts)
Reassembles a provider data source URI from its component paths (e.g.
bool saveLayerMetadata(const QString &providerKey, const QString &uri, const QgsLayerMetadata &metadata, QString &errorMessage) SIP_THROW(QgsNotSupportedException)
Saves metadata to the layer corresponding to the specified uri.
The QgsReadWriteLocker class is a convenience class that simplifies locking and unlocking QReadWriteL...
@ Write
Lock for write.
@ Read
Lock for read.
void changeMode(Mode mode)
Change the mode of the lock to mode.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
bool isNull() const
Test if the rectangle is null (all coordinates zero or after call to setMinimal()).
Definition: qgsrectangle.h:479
Contains information about the context of a rendering operation.
QgsExpressionContext & expressionContext()
Gets the expression context.
void setRendererScale(double scale)
Sets the renderer map scale.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:62
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
int renderingPass() const
Specifies the rendering pass in which this symbol layer should be rendered.
int layer() const
The layer of this symbol level.
QgsSymbol * symbol() const
The symbol of this symbol level.
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:93
QgsSymbolLayer * symbolLayer(int layer)
Returns the symbol layer at the specified index.
Definition: qgssymbol.cpp:423
int symbolLayerCount() const
Returns the total number of symbol layers contained in the symbol.
Definition: qgssymbol.h:215
DistanceUnit
Units of distance.
Definition: qgsunittypes.h:68
@ DistanceMeters
Meters.
Definition: qgsunittypes.h:69
RenderUnit
Rendering size units.
Definition: qgsunittypes.h:168
@ RenderMillimeters
Millimeters.
Definition: qgsunittypes.h:169
@ RenderMapUnits
Map units.
Definition: qgsunittypes.h:170
Interface to convert raw field values to their user-friendly value.
virtual QVariant convert(int fieldIdxInLayer, const QVariant &value)
Convert the provided value, for field fieldIdxInLayer.
virtual QgsVectorFileWriter::FieldValueConverter * clone() const
Creates a clone of the FieldValueConverter.
virtual QgsField fieldDefinition(const QgsField &field)
Returns a possibly modified field definition.
QgsVectorFileWriter::OptionType type
Options to pass to writeAsVectorFormat()
bool forceMulti
Sets to true to force creation of multi* geometries.
FieldNameSource fieldNameSource
Source for exported field names.
QgsCoordinateTransform ct
Transform to reproject exported geometries with, or invalid transform for no transformation.
QStringList attributesExportNames
Attributes export names.
QgsLayerMetadata layerMetadata
Layer metadata to save for the exported vector file.
QString layerName
Layer name. If let empty, it will be derived from the filename.
QgsRectangle filterExtent
If not empty, only features intersecting the extent will be saved.
QgsVectorFileWriter::FieldValueConverter * fieldValueConverter
Field value converter.
QStringList layerOptions
List of OGR layer creation options.
QgsVectorFileWriter::SymbologyExport symbologyExport
Symbology to export.
bool includeZ
Sets to true to include z dimension in output. This option is only valid if overrideGeometryType is s...
bool saveMetadata
Set to true to save layer metadata for the exported vector file.
QgsVectorFileWriter::ActionOnExistingFile actionOnExistingFile
Action on existing file.
QgsAttributeList attributes
Attributes to export (empty means all unless skipAttributeCreation is set)
bool onlySelectedFeatures
Write only selected features of layer.
bool skipAttributeCreation
Only write geometries.
QStringList datasourceOptions
List of OGR data source creation options.
QgsWkbTypes::Type overrideGeometryType
Set to a valid geometry type to override the default geometry type for the layer.
QgsFeedback * feedback
Optional feedback object allowing cancellation of layer save.
A convenience class for writing vector layers to disk based formats (e.g.
static QgsVectorFileWriter::WriterError writeAsVectorFormatV3(QgsVectorLayer *layer, const QString &fileName, const QgsCoordinateTransformContext &transformContext, const QgsVectorFileWriter::SaveVectorOptions &options, QString *errorMessage=nullptr, QString *newFilename=nullptr, QString *newLayer=nullptr)
Writes a layer out to a vector file.
QString lastError() const override
Returns the most recent error encountered by the sink, e.g.
@ CanAddNewFieldsToExistingLayer
Flag to indicate that new fields can be added to an existing layer. Imply CanAppendToExistingLayer.
@ CanAppendToExistingLayer
Flag to indicate that new features can be added to an existing layer.
@ CanAddNewLayer
Flag to indicate that a new layer can be added to the dataset.
@ CanDeleteLayer
Flag to indicate that an existing layer can be deleted.
static QgsVectorFileWriter::EditionCapabilities editionCapabilities(const QString &datasetName)
Returns edition capabilities for an existing dataset name.
static QgsVectorFileWriter * create(const QString &fileName, const QgsFields &fields, QgsWkbTypes::Type geometryType, const QgsCoordinateReferenceSystem &srs, const QgsCoordinateTransformContext &transformContext, const QgsVectorFileWriter::SaveVectorOptions &options, QgsFeatureSink::SinkFlags sinkFlags=QgsFeatureSink::SinkFlags(), QString *newFilename=nullptr, QString *newLayer=nullptr)
Create a new vector file writer.
bool addFeature(QgsFeature &feature, QgsFeatureSink::Flags flags=QgsFeatureSink::Flags()) override
Adds a single feature to the sink.
static bool supportsFeatureStyles(const QString &driverName)
Returns true if the specified driverName supports feature styles.
bool addFeatures(QgsFeatureList &features, QgsFeatureSink::Flags flags=QgsFeatureSink::Flags()) override
Adds a list of features to the sink.
double mSymbologyScale
Scale for symbology export (e.g. for symbols units in map units)
OGRGeometryH createEmptyGeometry(QgsWkbTypes::Type wkbType)
QMap< int, int > mAttrIdxToOgrIdx
Map attribute indizes to OGR field indexes.
@ Canceled
Writing was interrupted by manual cancellation.
@ ErrSavingMetadata
Metadata saving failed.
gdal::ogr_datasource_unique_ptr mDS
static Q_DECL_DEPRECATED QgsVectorFileWriter::WriterError writeAsVectorFormatV2(QgsVectorLayer *layer, const QString &fileName, const QgsCoordinateTransformContext &transformContext, const QgsVectorFileWriter::SaveVectorOptions &options, QString *newFilename=nullptr, QString *newLayer=nullptr, QString *errorMessage=nullptr)
Writes a layer out to a vector file.
~QgsVectorFileWriter() override
Close opened shapefile for writing.
static bool targetLayerExists(const QString &datasetName, const QString &layerName)
Returns whether the target layer already exists.
double symbologyScale() const
Returns the reference scale for output.
static QStringList defaultLayerOptions(const QString &driverName)
Returns a list of the default layer options for a specified driver.
SymbologyExport mSymbologyExport
static QString driverForExtension(const QString &extension)
Returns the OGR driver name for a specified file extension.
static QList< QgsVectorFileWriter::FilterFormatDetails > supportedFiltersAndFormats(VectorFormatOptions options=SortRecommended)
Returns a list or pairs, with format filter string as first element and OGR format key as second elem...
OGRSpatialReferenceH mOgrRef
static bool driverMetadata(const QString &driverName, MetaData &driverMetadata)
static bool deleteShapeFile(const QString &fileName)
Delete a shapefile (and its accompanying shx / dbf / prj / qix / qpj / cpg / sbn / sbx / idm / ind)
static QString filterForDriver(const QString &driverName)
Creates a filter for an OGR driver key.
QgsWkbTypes::Type mWkbType
Geometry type which is being used.
QgsVectorFileWriter::WriterError hasError() const
Checks whether there were any errors in constructor.
static OGRwkbGeometryType ogrTypeFromWkbType(QgsWkbTypes::Type type)
Gets the ogr geometry type from an internal QGIS wkb type enum.
@ SkipNonSpatialFormats
Filter out any formats which do not have spatial support (e.g. those which cannot save geometries)
static bool areThereNewFieldsToCreate(const QString &datasetName, const QString &layerName, QgsVectorLayer *layer, const QgsAttributeList &attributes)
Returns whether there are among the attributes specified some that do not exist yet in the layer.
static QList< QgsVectorFileWriter::DriverDetails > ogrDriverList(VectorFormatOptions options=SortRecommended)
Returns the driver list that can be used for dialogs.
static Q_DECL_DEPRECATED QgsVectorFileWriter::WriterError writeAsVectorFormat(QgsVectorLayer *layer, const QString &fileName, const QString &fileEncoding, const QgsCoordinateReferenceSystem &destCRS=QgsCoordinateReferenceSystem(), const QString &driverName="GPKG", bool onlySelected=false, QString *errorMessage=nullptr, const QStringList &datasourceOptions=QStringList(), const QStringList &layerOptions=QStringList(), bool skipAttributeCreation=false, QString *newFilename=nullptr, QgsVectorFileWriter::SymbologyExport symbologyExport=QgsVectorFileWriter::NoSymbology, double symbologyScale=1.0, const QgsRectangle *filterExtent=nullptr, QgsWkbTypes::Type overrideGeometryType=QgsWkbTypes::Unknown, bool forceMulti=false, bool includeZ=false, const QgsAttributeList &attributes=QgsAttributeList(), QgsVectorFileWriter::FieldValueConverter *fieldValueConverter=nullptr, QString *newLayer=nullptr)
Write contents of vector layer to an (OGR supported) vector format.
WriterError mError
Contains error value if construction was not successful.
FieldNameSource
Source for exported field names.
@ PreferAlias
Use the field alias as the exported field name, wherever one is set. Otherwise use the original field...
@ Original
Use original field names.
static QStringList defaultDatasetOptions(const QString &driverName)
Returns a list of the default dataset options for a specified driver.
static QStringList supportedFormatExtensions(VectorFormatOptions options=SortRecommended)
Returns a list of file extensions for supported formats, e.g "shp", "gpkg".
static QString convertCodecNameForEncodingOption(const QString &codecName)
Converts codec name to string passed to ENCODING layer creation option of OGR Shapefile.
FieldValueConverter * mFieldValueConverter
Field value converter.
bool addFeatureWithStyle(QgsFeature &feature, QgsFeatureRenderer *renderer, QgsUnitTypes::DistanceUnit outputUnit=QgsUnitTypes::DistanceMeters)
Adds a feature to the currently opened data source, using the style from a specified renderer.
QString errorMessage() const
Retrieves error message.
QgsVectorFileWriter::SymbologyExport symbologyExport() const
void setSymbologyScale(double scale)
Set reference scale for output.
Q_DECL_DEPRECATED QgsVectorFileWriter(const QString &vectorFileName, const QString &fileEncoding, const QgsFields &fields, QgsWkbTypes::Type geometryType, const QgsCoordinateReferenceSystem &srs=QgsCoordinateReferenceSystem(), const QString &driverName="GPKG", const QStringList &datasourceOptions=QStringList(), const QStringList &layerOptions=QStringList(), QString *newFilename=nullptr, QgsVectorFileWriter::SymbologyExport symbologyExport=QgsVectorFileWriter::NoSymbology, QgsFeatureSink::SinkFlags sinkFlags=QgsFeatureSink::SinkFlags(), QString *newLayer=nullptr, const QgsCoordinateTransformContext &transformContext=QgsCoordinateTransformContext(), FieldNameSource fieldNameSource=Original)
Create a new vector file writer.
QMap< QgsSymbolLayer *, QString > mSymbolLayerTable
static QString fileFilterString(VectorFormatOptions options=SortRecommended)
Returns filter string that can be used for dialogs.
ActionOnExistingFile
Combination of CanAddNewLayer, CanAppendToExistingLayer, CanAddNewFieldsToExistingLayer or CanDeleteL...
@ CreateOrOverwriteLayer
Create or overwrite layer.
@ CreateOrOverwriteFile
Create or overwrite file.
@ AppendToLayerNoNewFields
Append features to existing layer, but do not create new fields.
@ AppendToLayerAddFields
Append features to existing layer, and create new fields if needed.
Represents a vector layer which manages a vector based data sets.
Q_INVOKABLE QgsWkbTypes::Type wkbType() const FINAL
Returns the WKBType or WKBUnknown in case of error.
long long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
int selectedFeatureCount() const
Returns the number of features that are selected in this layer.
Q_INVOKABLE const QgsFeatureIds & selectedFeatureIds() const
Returns a list of the selected features IDs in this layer.
QString storageType() const
Returns the permanent storage type for this layer as a friendly name.
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider, it may be nullptr.
void minimumAndMaximumValue(int index, QVariant &minimum, QVariant &maximum) const
Calculates both the minimum and maximum value for an attribute column.
QgsFeatureRenderer * renderer()
Returns the feature renderer used for rendering the features in the layer in 2D map views.
static bool isMultiType(Type type) SIP_HOLDGIL
Returns true if the WKB type is a multi type.
Definition: qgswkbtypes.h:862
static bool hasM(Type type) SIP_HOLDGIL
Tests whether a WKB type contains m values.
Definition: qgswkbtypes.h:1130
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:70
@ GeometryCollectionZ
Definition: qgswkbtypes.h:93
static Type to25D(Type type) SIP_HOLDGIL
Will convert the 25D version of the flat type if supported or Unknown if not supported.
Definition: qgswkbtypes.h:1271
static Type singleType(Type type) SIP_HOLDGIL
Returns the single type for a WKB type.
Definition: qgswkbtypes.h:157
static Type flatType(Type type) SIP_HOLDGIL
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:732
static Type multiType(Type type) SIP_HOLDGIL
Returns the multi type for a WKB type.
Definition: qgswkbtypes.h:304
static Type addZ(Type type) SIP_HOLDGIL
Adds the z dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1176
static bool hasZ(Type type) SIP_HOLDGIL
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:1080
std::unique_ptr< std::remove_pointer< OGRFeatureH >::type, OGRFeatureDeleter > ogr_feature_unique_ptr
Scoped OGR feature.
Definition: qgsogrutils.h:134
std::unique_ptr< std::remove_pointer< OGRDataSourceH >::type, OGRDataSourceDeleter > ogr_datasource_unique_ptr
Scoped OGR data source.
Definition: qgsogrutils.h:119
std::unique_ptr< std::remove_pointer< OGRFieldDefnH >::type, OGRFldDeleter > ogr_field_def_unique_ptr
Scoped OGR field definition.
Definition: qgsogrutils.h:129
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
#define FALLTHROUGH
Definition: qgis.h:2572
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:2545
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:2544
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