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