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