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