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