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