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