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