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