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