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