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