QGIS API Documentation 3.39.0-Master (d85f3c2a281)
Loading...
Searching...
No Matches
qgsvectorfilewriter.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsvectorfilewriter.cpp
3 generic vector file writer
4 -------------------
5 begin : Sat Jun 16 2004
6 copyright : (C) 2004 by Tim Sutton
7 email : tim at linfiniti.com
8 ***************************************************************************/
9
10/***************************************************************************
11 * *
12 * This program is free software; you can redistribute it and/or modify *
13 * it under the terms of the GNU General Public License as published by *
14 * the Free Software Foundation; either version 2 of the License, or *
15 * (at your option) any later version. *
16 * *
17 ***************************************************************************/
18
19#include "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
64
65QVariant QgsVectorFileWriter::FieldValueConverter::convert( int /*fieldIdxInLayer*/, const QVariant &value )
66{
67 return value;
68}
69
74
75QgsVectorFileWriter::QgsVectorFileWriter( const QString &vectorFileName,
76 const QString &fileEncoding,
77 const QgsFields &fields,
78 Qgis::WkbType geometryType,
80 const QString &driverName,
81 const QStringList &datasourceOptions,
82 const QStringList &layerOptions,
83 QString *newFilename,
86 QString *newLayer,
87 const QgsCoordinateTransformContext &transformContext,
88 FieldNameSource fieldNameSource )
89 : mError( NoError )
90 , mWkbType( geometryType )
92 , mSymbologyScale( 1.0 )
93{
94 init( vectorFileName, fileEncoding, fields, geometryType,
95 srs, driverName, datasourceOptions, layerOptions, newFilename, nullptr,
96 QString(), CreateOrOverwriteFile, newLayer, sinkFlags, transformContext, fieldNameSource, nullptr );
97}
98
99QgsVectorFileWriter::QgsVectorFileWriter( const QString &vectorFileName,
100 const QString &fileEncoding,
101 const QgsFields &fields,
102 Qgis::WkbType geometryType,
104 const QString &driverName,
105 const QStringList &datasourceOptions,
106 const QStringList &layerOptions,
107 QString *newFilename,
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].toUtf8().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].toUtf8().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;
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 QMetaType::Type::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 QMetaType::Type::QString:
659 ogrType = OFTString;
660 if ( ( ogrWidth <= 0 || ogrWidth > 255 ) && mOgrDriverName == QLatin1String( "ESRI Shapefile" ) )
661 ogrWidth = 255;
662 break;
663
664 case QMetaType::Type::Int:
665 ogrType = OFTInteger;
666 ogrWidth = ogrWidth > 0 && ogrWidth <= 10 ? ogrWidth : 10;
667 ogrPrecision = 0;
668 break;
669
670 case QMetaType::Type::Bool:
671 ogrType = OFTInteger;
672 ogrSubType = OFSTBoolean;
673 ogrWidth = 1;
674 ogrPrecision = 0;
675 break;
676
677 case QMetaType::Type::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 QMetaType::Type::QDate:
690 ogrType = OFTDate;
691 break;
692
693 case QMetaType::Type::QTime:
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 QMetaType::Type::QDateTime:
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 QMetaType::Type::QByteArray:
718 ogrType = OFTBinary;
719 break;
720
721 case QMetaType::Type::QStringList:
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( QMetaType::Type::QString );
736 }
737 else
738 {
739 ogrType = OFTString;
740 ogrWidth = 255;
741 }
742 break;
743 }
744
745 case QMetaType::Type::QVariantMap:
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 QMetaType::Type::QVariantList:
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() == QMetaType::Type::QString )
771 {
772 const char *pszDataTypes = GDALGetMetadataItem( poDriver, GDAL_DMD_CREATIONFIELDDATATYPES, nullptr );
773 if ( pszDataTypes && strstr( pszDataTypes, "StringList" ) )
774 {
775 ogrType = OFTStringList;
776 mSupportedListSubTypes.insert( QMetaType::Type::QString );
777 }
778 else
779 {
780 ogrType = OFTString;
781 ogrWidth = 255;
782 }
783 break;
784 }
785 else if ( attrField.subType() == QMetaType::Type::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( QMetaType::Type::Int );
792 }
793 else
794 {
795 ogrType = OFTString;
796 ogrWidth = 255;
797 }
798 break;
799 }
800 else if ( attrField.subType() == QMetaType::Type::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( QMetaType::Type::Double );
807 }
808 else
809 {
810 ogrType = OFTString;
811 ogrWidth = 255;
812 }
813 break;
814 }
815 else if ( attrField.subType() == QMetaType::Type::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( QMetaType::Type::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 // Comma Separated Value
1120 datasetOptions.clear();
1121 layerOptions.clear();
1122
1123 layerOptions.insert( QStringLiteral( "LINEFORMAT" ), new QgsVectorFileWriter::SetOption(
1124 QObject::tr( "By default when creating new .csv files they "
1125 "are created with the line termination conventions "
1126 "of the local platform (CR/LF on Win32 or LF on all other systems). "
1127 "This may be overridden through the use of the LINEFORMAT option." ),
1128 QStringList()
1129 << QStringLiteral( "CRLF" )
1130 << QStringLiteral( "LF" ),
1131 QString(), // Default value
1132 true // Allow None
1133 ) );
1134
1135 layerOptions.insert( QStringLiteral( "GEOMETRY" ), new QgsVectorFileWriter::SetOption(
1136 QObject::tr( "By default, the geometry of a feature written to a .csv file is discarded. "
1137 "It is possible to export the geometry in its WKT representation by "
1138 "specifying GEOMETRY=AS_WKT. It is also possible to export point geometries "
1139 "into their X,Y,Z components by specifying GEOMETRY=AS_XYZ, GEOMETRY=AS_XY "
1140 "or GEOMETRY=AS_YX." ),
1141 QStringList()
1142 << QStringLiteral( "AS_WKT" )
1143 << QStringLiteral( "AS_XYZ" )
1144 << QStringLiteral( "AS_XY" )
1145 << QStringLiteral( "AS_YX" ),
1146 QString(), // Default value
1147 true // Allow None
1148 ) );
1149
1150 layerOptions.insert( QStringLiteral( "CREATE_CSVT" ), new QgsVectorFileWriter::BoolOption(
1151 QObject::tr( "Create the associated .csvt file to describe the type of each "
1152 "column of the layer and its optional width and precision." ),
1153 false // Default value
1154 ) );
1155
1156 layerOptions.insert( QStringLiteral( "SEPARATOR" ), new QgsVectorFileWriter::SetOption(
1157 QObject::tr( "Field separator character." ),
1158 QStringList()
1159 << QStringLiteral( "COMMA" )
1160 << QStringLiteral( "SEMICOLON" )
1161 << QStringLiteral( "TAB" ),
1162 QStringLiteral( "COMMA" ) // Default value
1163 ) );
1164
1165 layerOptions.insert( QStringLiteral( "STRING_QUOTING" ), new QgsVectorFileWriter::SetOption(
1166 QObject::tr( "Double-quote strings. IF_AMBIGUOUS means that string values that look like numbers will be quoted." ),
1167 QStringList()
1168 << QStringLiteral( "IF_NEEDED" )
1169 << QStringLiteral( "IF_AMBIGUOUS" )
1170 << QStringLiteral( "ALWAYS" ),
1171 QStringLiteral( "IF_AMBIGUOUS" ) // Default value
1172 ) );
1173
1174 layerOptions.insert( QStringLiteral( "WRITE_BOM" ), new QgsVectorFileWriter::BoolOption(
1175 QObject::tr( "Write a UTF-8 Byte Order Mark (BOM) at the start of the file." ),
1176 false // Default value
1177 ) );
1178
1179 driverMetadata.insert( QStringLiteral( "CSV" ),
1181 QStringLiteral( "Comma Separated Value [CSV]" ),
1182 QObject::tr( "Comma Separated Value [CSV]" ),
1183 QStringLiteral( "*.csv" ),
1184 QStringLiteral( "csv" ),
1185 datasetOptions,
1186 layerOptions
1187 )
1188 );
1189
1190 // FlatGeobuf
1191 datasetOptions.clear();
1192 layerOptions.clear();
1193
1194 driverMetadata.insert( QStringLiteral( "FlatGeobuf" ),
1196 QStringLiteral( "FlatGeobuf" ),
1197 QObject::tr( "FlatGeobuf" ),
1198 QStringLiteral( "*.fgb" ),
1199 QStringLiteral( "fgb" ),
1200 datasetOptions,
1201 layerOptions,
1202 QStringLiteral( "UTF-8" )
1203 )
1204 );
1205
1206 // ESRI Shapefile
1207 datasetOptions.clear();
1208 layerOptions.clear();
1209
1210 layerOptions.insert( QStringLiteral( "SHPT" ), new QgsVectorFileWriter::SetOption(
1211 QObject::tr( "Override the type of shapefile created. "
1212 "Can be one of NULL for a simple .dbf file with no .shp file, POINT, "
1213 "ARC, POLYGON or MULTIPOINT for 2D, or POINTZ, ARCZ, POLYGONZ or "
1214 "MULTIPOINTZ for 3D;" ) +
1215 QObject::tr( " POINTM, ARCM, POLYGONM or MULTIPOINTM for measured geometries"
1216 " and POINTZM, ARCZM, POLYGONZM or MULTIPOINTZM for 3D measured"
1217 " geometries." ) +
1218 QObject::tr( " MULTIPATCH files are supported since GDAL 2.2." ) +
1219 ""
1220 , QStringList()
1221 << QStringLiteral( "NULL" )
1222 << QStringLiteral( "POINT" )
1223 << QStringLiteral( "ARC" )
1224 << QStringLiteral( "POLYGON" )
1225 << QStringLiteral( "MULTIPOINT" )
1226 << QStringLiteral( "POINTZ" )
1227 << QStringLiteral( "ARCZ" )
1228 << QStringLiteral( "POLYGONZ" )
1229 << QStringLiteral( "MULTIPOINTZ" )
1230 << QStringLiteral( "POINTM" )
1231 << QStringLiteral( "ARCM" )
1232 << QStringLiteral( "POLYGONM" )
1233 << QStringLiteral( "MULTIPOINTM" )
1234 << QStringLiteral( "POINTZM" )
1235 << QStringLiteral( "ARCZM" )
1236 << QStringLiteral( "POLYGONZM" )
1237 << QStringLiteral( "MULTIPOINTZM" )
1238 << QStringLiteral( "MULTIPATCH" )
1239 << QString(),
1240 QString(), // Default value
1241 true // Allow None
1242 ) );
1243
1244 // there does not seem to be a reason to provide this option to the user again
1245 // as we set encoding for shapefiles based on "fileEncoding" parameter passed to the writer
1246#if 0
1247 layerOptions.insert( "ENCODING", new QgsVectorFileWriter::SetOption(
1248 QObject::tr( "Set the encoding value in the DBF file. "
1249 "The default value is LDID/87. It is not clear "
1250 "what other values may be appropriate." ),
1251 QStringList()
1252 << "LDID/87",
1253 "LDID/87" // Default value
1254 ) );
1255#endif
1256
1257 layerOptions.insert( QStringLiteral( "RESIZE" ), new QgsVectorFileWriter::BoolOption(
1258 QObject::tr( "Set to YES to resize fields to their optimal size." ),
1259 false // Default value
1260 ) );
1261
1262 driverMetadata.insert( QStringLiteral( "ESRI" ),
1264 QStringLiteral( "ESRI Shapefile" ),
1265 QObject::tr( "ESRI Shapefile" ),
1266 QStringLiteral( "*.shp" ),
1267 QStringLiteral( "shp" ),
1268 datasetOptions,
1269 layerOptions
1270 )
1271 );
1272
1273 // DBF File
1274 datasetOptions.clear();
1275 layerOptions.clear();
1276
1277 driverMetadata.insert( QStringLiteral( "DBF File" ),
1279 QStringLiteral( "DBF File" ),
1280 QObject::tr( "DBF File" ),
1281 QStringLiteral( "*.dbf" ),
1282 QStringLiteral( "dbf" ),
1283 datasetOptions,
1284 layerOptions
1285 )
1286 );
1287
1288 // GeoJSON
1289 datasetOptions.clear();
1290 layerOptions.clear();
1291
1292 layerOptions.insert( QStringLiteral( "WRITE_BBOX" ), new QgsVectorFileWriter::BoolOption(
1293 QObject::tr( "Set to YES to write a bbox property with the bounding box "
1294 "of the geometries at the feature and feature collection level." ),
1295 false // Default value
1296 ) );
1297
1298 layerOptions.insert( QStringLiteral( "COORDINATE_PRECISION" ), new QgsVectorFileWriter::IntOption(
1299 QObject::tr( "Maximum number of figures after decimal separator to write in coordinates. "
1300 "Defaults to 15. Truncation will occur to remove trailing zeros." ),
1301 15 // Default value
1302 ) );
1303
1304 layerOptions.insert( QStringLiteral( "RFC7946" ), new QgsVectorFileWriter::BoolOption(
1305 QObject::tr( "Whether to use RFC 7946 standard. "
1306 "If disabled GeoJSON 2008 initial version will be used. "
1307 "Default is NO (thus GeoJSON 2008). See also Documentation (via Help button)" ),
1308 false // Default value
1309 ) );
1310
1311 driverMetadata.insert( QStringLiteral( "GeoJSON" ),
1313 QStringLiteral( "GeoJSON" ),
1314 QObject::tr( "GeoJSON" ),
1315 QStringLiteral( "*.geojson" ),
1316 QStringLiteral( "geojson" ),
1317 datasetOptions,
1318 layerOptions,
1319 QStringLiteral( "UTF-8" )
1320 )
1321 );
1322
1323 // GeoJSONSeq
1324 datasetOptions.clear();
1325 layerOptions.clear();
1326
1327 layerOptions.insert( QStringLiteral( "COORDINATE_PRECISION" ), new QgsVectorFileWriter::IntOption(
1328 QObject::tr( "Maximum number of figures after decimal separator to write in coordinates. "
1329 "Defaults to 15. Truncation will occur to remove trailing zeros." ),
1330 15 // Default value
1331 ) );
1332
1333 layerOptions.insert( QStringLiteral( "RS" ), new QgsVectorFileWriter::BoolOption(
1334 QObject::tr( "Whether to start records with the RS=0x1E character (RFC 8142 standard). "
1335 "Defaults to NO: Newline Delimited JSON (geojsonl). \n"
1336 "If set to YES: RFC 8142 standard: GeoJSON Text Sequences (geojsons)." ),
1337 false // Default value = NO
1338 ) );
1339
1340 driverMetadata.insert( QStringLiteral( "GeoJSONSeq" ),
1342 QStringLiteral( "GeoJSON - Newline Delimited" ),
1343 QObject::tr( "GeoJSON - Newline Delimited" ),
1344 QStringLiteral( "*.geojsonl *.geojsons *.json" ),
1345 QStringLiteral( "geojsonl geojsons json" ),
1346 datasetOptions,
1347 layerOptions,
1348 QStringLiteral( "UTF-8" )
1349 )
1350 );
1351
1352 // GeoRSS
1353 datasetOptions.clear();
1354 layerOptions.clear();
1355
1356 datasetOptions.insert( QStringLiteral( "FORMAT" ), new QgsVectorFileWriter::SetOption(
1357 QObject::tr( "whether the document must be in RSS 2.0 or Atom 1.0 format. "
1358 "Default value : RSS" ),
1359 QStringList()
1360 << QStringLiteral( "RSS" )
1361 << QStringLiteral( "ATOM" ),
1362 QStringLiteral( "RSS" ) // Default value
1363 ) );
1364
1365 datasetOptions.insert( QStringLiteral( "GEOM_DIALECT" ), new QgsVectorFileWriter::SetOption(
1366 QObject::tr( "The encoding of location information. Default value : SIMPLE. "
1367 "W3C_GEO only supports point geometries. "
1368 "SIMPLE or W3C_GEO only support geometries in geographic WGS84 coordinates." ),
1369 QStringList()
1370 << QStringLiteral( "SIMPLE" )
1371 << QStringLiteral( "GML" )
1372 << QStringLiteral( "W3C_GEO" ),
1373 QStringLiteral( "SIMPLE" ) // Default value
1374 ) );
1375
1376 datasetOptions.insert( QStringLiteral( "USE_EXTENSIONS" ), new QgsVectorFileWriter::BoolOption(
1377 QObject::tr( "If defined to YES, extension fields will be written. "
1378 "If the field name not found in the base schema matches "
1379 "the foo_bar pattern, foo will be considered as the namespace "
1380 "of the element, and a <foo:bar> element will be written. "
1381 "Otherwise, elements will be written in the <ogr:> namespace." ),
1382 false // Default value
1383 ) );
1384
1385 datasetOptions.insert( QStringLiteral( "WRITE_HEADER_AND_FOOTER" ), new QgsVectorFileWriter::BoolOption(
1386 QObject::tr( "If defined to NO, only <entry> or <item> elements will be written. "
1387 "The user will have to provide the appropriate header and footer of the document." ),
1388 true // Default value
1389 ) );
1390
1391 datasetOptions.insert( QStringLiteral( "HEADER" ), new QgsVectorFileWriter::StringOption(
1392 QObject::tr( "XML content that will be put between the <channel> element and the "
1393 "first <item> element for a RSS document, or between the xml tag and "
1394 "the first <entry> element for an Atom document." ),
1395 QString() // Default value
1396 ) );
1397
1398 datasetOptions.insert( QStringLiteral( "TITLE" ), new QgsVectorFileWriter::StringOption(
1399 QObject::tr( "Value put inside the <title> element in the header. "
1400 "If not provided, a dummy value will be used as that element is compulsory." ),
1401 QString() // Default value
1402 ) );
1403
1404 datasetOptions.insert( QStringLiteral( "DESCRIPTION" ), new QgsVectorFileWriter::StringOption(
1405 QObject::tr( "Value put inside the <description> element in the header. "
1406 "If not provided, a dummy value will be used as that element is compulsory." ),
1407 QString() // Default value
1408 ) );
1409
1410 datasetOptions.insert( QStringLiteral( "LINK" ), new QgsVectorFileWriter::StringOption(
1411 QObject::tr( "Value put inside the <link> element in the header. "
1412 "If not provided, a dummy value will be used as that element is compulsory." ),
1413 QString() // Default value
1414 ) );
1415
1416 datasetOptions.insert( QStringLiteral( "UPDATED" ), new QgsVectorFileWriter::StringOption(
1417 QObject::tr( "Value put inside the <updated> element in the header. "
1418 "Should be formatted as a XML datetime. "
1419 "If not provided, a dummy value will be used as that element is compulsory." ),
1420 QString() // Default value
1421 ) );
1422
1423 datasetOptions.insert( QStringLiteral( "AUTHOR_NAME" ), new QgsVectorFileWriter::StringOption(
1424 QObject::tr( "Value put inside the <author><name> element in the header. "
1425 "If not provided, a dummy value will be used as that element is compulsory." ),
1426 QString() // Default value
1427 ) );
1428
1429 datasetOptions.insert( QStringLiteral( "ID" ), new QgsVectorFileWriter::StringOption(
1430 QObject::tr( "Value put inside the <id> element in the header. "
1431 "If not provided, a dummy value will be used as that element is compulsory." ),
1432 QString() // Default value
1433 ) );
1434
1435 driverMetadata.insert( QStringLiteral( "GeoRSS" ),
1437 QStringLiteral( "GeoRSS" ),
1438 QObject::tr( "GeoRSS" ),
1439 QStringLiteral( "*.xml" ),
1440 QStringLiteral( "xml" ),
1441 datasetOptions,
1442 layerOptions,
1443 QStringLiteral( "UTF-8" )
1444 )
1445 );
1446
1447 // Geography Markup Language [GML]
1448 datasetOptions.clear();
1449 layerOptions.clear();
1450
1451 datasetOptions.insert( QStringLiteral( "XSISCHEMAURI" ), new QgsVectorFileWriter::StringOption(
1452 QObject::tr( "If provided, this URI will be inserted as the schema location. "
1453 "Note that the schema file isn't actually accessed by OGR, so it "
1454 "is up to the user to ensure it will match the schema of the OGR "
1455 "produced GML data file." ),
1456 QString() // Default value
1457 ) );
1458
1459 datasetOptions.insert( QStringLiteral( "XSISCHEMA" ), new QgsVectorFileWriter::SetOption(
1460 QObject::tr( "This writes a GML application schema file to a corresponding "
1461 ".xsd file (with the same basename). If INTERNAL is used the "
1462 "schema is written within the GML file, but this is experimental "
1463 "and almost certainly not valid XML. "
1464 "OFF disables schema generation (and is implicit if XSISCHEMAURI is used)." ),
1465 QStringList()
1466 << QStringLiteral( "EXTERNAL" )
1467 << QStringLiteral( "INTERNAL" )
1468 << QStringLiteral( "OFF" ),
1469 QStringLiteral( "EXTERNAL" ) // Default value
1470 ) );
1471
1472 datasetOptions.insert( QStringLiteral( "PREFIX" ), new QgsVectorFileWriter::StringOption(
1473 QObject::tr( "This is the prefix for the application target namespace." ),
1474 QStringLiteral( "ogr" ) // Default value
1475 ) );
1476
1477 datasetOptions.insert( QStringLiteral( "STRIP_PREFIX" ), new QgsVectorFileWriter::BoolOption(
1478 QObject::tr( "Can be set to TRUE to avoid writing the prefix of the "
1479 "application target namespace in the GML file." ),
1480 false // Default value
1481 ) );
1482
1483 datasetOptions.insert( QStringLiteral( "TARGET_NAMESPACE" ), new QgsVectorFileWriter::StringOption(
1484 QObject::tr( "Defaults to 'http://ogr.maptools.org/'. "
1485 "This is the application target namespace." ),
1486 QStringLiteral( "http://ogr.maptools.org/" ) // Default value
1487 ) );
1488
1489 datasetOptions.insert( QStringLiteral( "FORMAT" ), new QgsVectorFileWriter::SetOption(
1490 QObject::tr( "GML version to use." ),
1491 QStringList()
1492 << QStringLiteral( "GML2" )
1493 << QStringLiteral( "GML3" )
1494 << QStringLiteral( "GML3Deegree" )
1495 << QStringLiteral( "GML3.2" ),
1496 QStringLiteral( "GML3.2" ) // Default value
1497 ) );
1498
1499 datasetOptions.insert( QStringLiteral( "GML3_LONGSRS" ), new QgsVectorFileWriter::BoolOption(
1500 QObject::tr( "Only valid when FORMAT=GML3/GML3Degree/GML3.2. Default to YES. " //needs review here
1501 "If YES, SRS with EPSG authority will be written with the "
1502 "'urn:ogc:def:crs:EPSG::' prefix. In the case the SRS is a "
1503 "geographic SRS without explicit AXIS order, but that the same "
1504 "SRS authority code imported with ImportFromEPSGA() should be "
1505 "treated as lat/long, then the function will take care of coordinate "
1506 "order swapping. If set to NO, SRS with EPSG authority will be "
1507 "written with the 'EPSG:' prefix, even if they are in lat/long order." ),
1508 true // Default value
1509 ) );
1510
1511 datasetOptions.insert( QStringLiteral( "WRITE_FEATURE_BOUNDED_BY" ), new QgsVectorFileWriter::BoolOption(
1512 QObject::tr( "only valid when FORMAT=GML3/GML3Degree/GML3.2) Default to YES. "
1513 "If set to NO, the <gml:boundedBy> element will not be written for "
1514 "each feature." ),
1515 true // Default value
1516 ) );
1517
1518 datasetOptions.insert( QStringLiteral( "SPACE_INDENTATION" ), new QgsVectorFileWriter::BoolOption(
1519 QObject::tr( "Default to YES. If YES, the output will be indented with spaces "
1520 "for more readability, but at the expense of file size." ),
1521 true // Default value
1522 ) );
1523
1524
1525 driverMetadata.insert( QStringLiteral( "GML" ),
1527 QStringLiteral( "Geography Markup Language [GML]" ),
1528 QObject::tr( "Geography Markup Language [GML]" ),
1529 QStringLiteral( "*.gml" ),
1530 QStringLiteral( "gml" ),
1531 datasetOptions,
1532 layerOptions,
1533 QStringLiteral( "UTF-8" )
1534 )
1535 );
1536
1537 // GeoPackage
1538 datasetOptions.clear();
1539 layerOptions.clear();
1540
1541 layerOptions.insert( QStringLiteral( "IDENTIFIER" ), new QgsVectorFileWriter::StringOption(
1542 QObject::tr( "Human-readable identifier (e.g. short name) for the layer content" ),
1543 QString() // Default value
1544 ) );
1545
1546 layerOptions.insert( QStringLiteral( "DESCRIPTION" ), new QgsVectorFileWriter::StringOption(
1547 QObject::tr( "Human-readable description for the layer content" ),
1548 QString() // Default value
1549 ) );
1550
1551 layerOptions.insert( QStringLiteral( "FID" ), new QgsVectorFileWriter::StringOption(
1552 QObject::tr( "Name for the feature identifier column" ),
1553 QStringLiteral( "fid" ) // Default value
1554 ) );
1555
1556 layerOptions.insert( QStringLiteral( "GEOMETRY_NAME" ), new QgsVectorFileWriter::StringOption(
1557 QObject::tr( "Name for the geometry column" ),
1558 QStringLiteral( "geom" ) // Default value
1559 ) );
1560
1561 layerOptions.insert( QStringLiteral( "SPATIAL_INDEX" ), new QgsVectorFileWriter::BoolOption(
1562 QObject::tr( "If a spatial index must be created." ),
1563 true // Default value
1564 ) );
1565
1566 driverMetadata.insert( QStringLiteral( "GPKG" ),
1568 QStringLiteral( "GeoPackage" ),
1569 QObject::tr( "GeoPackage" ),
1570 QStringLiteral( "*.gpkg" ),
1571 QStringLiteral( "gpkg" ),
1572 datasetOptions,
1573 layerOptions,
1574 QStringLiteral( "UTF-8" )
1575 )
1576 );
1577
1578 // Generic Mapping Tools [GMT]
1579 datasetOptions.clear();
1580 layerOptions.clear();
1581
1582 driverMetadata.insert( QStringLiteral( "GMT" ),
1584 QStringLiteral( "Generic Mapping Tools [GMT]" ),
1585 QObject::tr( "Generic Mapping Tools [GMT]" ),
1586 QStringLiteral( "*.gmt" ),
1587 QStringLiteral( "gmt" ),
1588 datasetOptions,
1589 layerOptions
1590 )
1591 );
1592
1593 // GPS eXchange Format [GPX]
1594 datasetOptions.clear();
1595 layerOptions.clear();
1596
1597 layerOptions.insert( QStringLiteral( "FORCE_GPX_TRACK" ), new QgsVectorFileWriter::BoolOption(
1598 QObject::tr( "By default when writing a layer whose features are of "
1599 "type wkbLineString, the GPX driver chooses to write "
1600 "them as routes. If FORCE_GPX_TRACK=YES is specified, "
1601 "they will be written as tracks." ),
1602 false // Default value
1603 ) );
1604
1605 layerOptions.insert( QStringLiteral( "FORCE_GPX_ROUTE" ), new QgsVectorFileWriter::BoolOption(
1606 QObject::tr( "By default when writing a layer whose features are of "
1607 "type wkbMultiLineString, the GPX driver chooses to write "
1608 "them as tracks. If FORCE_GPX_ROUTE=YES is specified, "
1609 "they will be written as routes, provided that the multilines "
1610 "are composed of only one single line." ),
1611 false // Default value
1612 ) );
1613
1614 datasetOptions.insert( QStringLiteral( "GPX_USE_EXTENSIONS" ), new QgsVectorFileWriter::BoolOption(
1615 QObject::tr( "If GPX_USE_EXTENSIONS=YES is specified, "
1616 "extra fields will be written inside the <extensions> tag." ),
1617 false // Default value
1618 ) );
1619
1620 datasetOptions.insert( QStringLiteral( "GPX_EXTENSIONS_NS" ), new QgsVectorFileWriter::StringOption(
1621 QObject::tr( "Only used if GPX_USE_EXTENSIONS=YES and GPX_EXTENSIONS_NS_URL "
1622 "is set. The namespace value used for extension tags. By default, 'ogr'." ),
1623 QStringLiteral( "ogr" ) // Default value
1624 ) );
1625
1626 datasetOptions.insert( QStringLiteral( "GPX_EXTENSIONS_NS_URL" ), new QgsVectorFileWriter::StringOption(
1627 QObject::tr( "Only used if GPX_USE_EXTENSIONS=YES and GPX_EXTENSIONS_NS "
1628 "is set. The namespace URI. By default, 'http://osgeo.org/gdal'." ),
1629 QStringLiteral( "http://osgeo.org/gdal" ) // Default value
1630 ) );
1631
1632 datasetOptions.insert( QStringLiteral( "LINEFORMAT" ), new QgsVectorFileWriter::SetOption(
1633 QObject::tr( "By default files are created with the line termination "
1634 "conventions of the local platform (CR/LF on win32 or LF "
1635 "on all other systems). This may be overridden through use "
1636 "of the LINEFORMAT layer creation option which may have a value "
1637 "of CRLF (DOS format) or LF (Unix format)." ),
1638 QStringList()
1639 << QStringLiteral( "CRLF" )
1640 << QStringLiteral( "LF" ),
1641 QString(), // Default value
1642 true // Allow None
1643 ) );
1644
1645 driverMetadata.insert( QStringLiteral( "GPX" ),
1647 QStringLiteral( "GPS eXchange Format [GPX]" ),
1648 QObject::tr( "GPS eXchange Format [GPX]" ),
1649 QStringLiteral( "*.gpx" ),
1650 QStringLiteral( "gpx" ),
1651 datasetOptions,
1652 layerOptions,
1653 QStringLiteral( "UTF-8" )
1654 )
1655 );
1656
1657 // INTERLIS 1
1658 datasetOptions.clear();
1659 layerOptions.clear();
1660
1661 driverMetadata.insert( QStringLiteral( "Interlis 1" ),
1663 QStringLiteral( "INTERLIS 1" ),
1664 QObject::tr( "INTERLIS 1" ),
1665 QStringLiteral( "*.itf *.xml *.ili" ),
1666 QStringLiteral( "ili" ),
1667 datasetOptions,
1668 layerOptions
1669 )
1670 );
1671
1672 // INTERLIS 2
1673 datasetOptions.clear();
1674 layerOptions.clear();
1675
1676 driverMetadata.insert( QStringLiteral( "Interlis 2" ),
1678 QStringLiteral( "INTERLIS 2" ),
1679 QObject::tr( "INTERLIS 2" ),
1680 QStringLiteral( "*.xtf *.xml *.ili" ),
1681 QStringLiteral( "ili" ),
1682 datasetOptions,
1683 layerOptions
1684 )
1685 );
1686
1687 // Keyhole Markup Language [KML]
1688 datasetOptions.clear();
1689 layerOptions.clear();
1690
1691 datasetOptions.insert( QStringLiteral( "NameField" ), new QgsVectorFileWriter::StringOption(
1692 QObject::tr( "Allows you to specify the field to use for the KML <name> element." ),
1693 QStringLiteral( "Name" ) // Default value
1694 ) );
1695
1696 datasetOptions.insert( QStringLiteral( "DescriptionField" ), new QgsVectorFileWriter::StringOption(
1697 QObject::tr( "Allows you to specify the field to use for the KML <description> element." ),
1698 QStringLiteral( "Description" ) // Default value
1699 ) );
1700
1701 datasetOptions.insert( QStringLiteral( "AltitudeMode" ), new QgsVectorFileWriter::SetOption(
1702 QObject::tr( "Allows you to specify the AltitudeMode to use for KML geometries. "
1703 "This will only affect 3D geometries and must be one of the valid KML options." ),
1704 QStringList()
1705 << QStringLiteral( "clampToGround" )
1706 << QStringLiteral( "relativeToGround" )
1707 << QStringLiteral( "absolute" ),
1708 QStringLiteral( "relativeToGround" ) // Default value
1709 ) );
1710
1711 datasetOptions.insert( QStringLiteral( "DOCUMENT_ID" ), new QgsVectorFileWriter::StringOption(
1712 QObject::tr( "The DOCUMENT_ID datasource creation option can be used to specified "
1713 "the id of the root <Document> node. The default value is root_doc." ),
1714 QStringLiteral( "root_doc" ) // Default value
1715 ) );
1716
1717 driverMetadata.insert( QStringLiteral( "KML" ),
1719 QStringLiteral( "Keyhole Markup Language [KML]" ),
1720 QObject::tr( "Keyhole Markup Language [KML]" ),
1721 QStringLiteral( "*.kml" ),
1722 QStringLiteral( "kml" ),
1723 datasetOptions,
1724 layerOptions,
1725 QStringLiteral( "UTF-8" )
1726 )
1727 );
1728
1729 // Mapinfo
1730 datasetOptions.clear();
1731 layerOptions.clear();
1732
1733 auto insertMapInfoOptions = []( QMap<QString, QgsVectorFileWriter::Option *> &datasetOptions, QMap<QString, QgsVectorFileWriter::Option *> &layerOptions )
1734 {
1735 datasetOptions.insert( QStringLiteral( "SPATIAL_INDEX_MODE" ), new QgsVectorFileWriter::SetOption(
1736 QObject::tr( "Use this to turn on 'quick spatial index mode'. "
1737 "In this mode writing files can be about 5 times faster, "
1738 "but spatial queries can be up to 30 times slower." ),
1739 QStringList()
1740 << QStringLiteral( "QUICK" )
1741 << QStringLiteral( "OPTIMIZED" ),
1742 QStringLiteral( "QUICK" ), // Default value
1743 true // Allow None
1744 ) );
1745
1746 datasetOptions.insert( QStringLiteral( "BLOCK_SIZE" ), new QgsVectorFileWriter::IntOption(
1747 QObject::tr( "(multiples of 512): Block size for .map files. Defaults "
1748 "to 512. MapInfo 15.2 and above creates .tab files with a "
1749 "blocksize of 16384 bytes. Any MapInfo version should be "
1750 "able to handle block sizes from 512 to 32256." ),
1751 512
1752 ) );
1753 layerOptions.insert( QStringLiteral( "BOUNDS" ), new QgsVectorFileWriter::StringOption(
1754 QObject::tr( "xmin,ymin,xmax,ymax: Define custom layer bounds to increase the "
1755 "accuracy of the coordinates. Note: the geometry of written "
1756 "features must be within the defined box." ),
1757 QString() // Default value
1758 ) );
1759 };
1760 insertMapInfoOptions( datasetOptions, layerOptions );
1761
1762 driverMetadata.insert( QStringLiteral( "MapInfo File" ),
1764 QStringLiteral( "Mapinfo" ),
1765 QObject::tr( "Mapinfo TAB" ),
1766 QStringLiteral( "*.tab" ),
1767 QStringLiteral( "tab" ),
1768 datasetOptions,
1769 layerOptions
1770 )
1771 );
1772 datasetOptions.clear();
1773 layerOptions.clear();
1774 insertMapInfoOptions( datasetOptions, layerOptions );
1775
1776 // QGIS internal alias for MIF files
1777 driverMetadata.insert( QStringLiteral( "MapInfo MIF" ),
1779 QStringLiteral( "Mapinfo" ),
1780 QObject::tr( "Mapinfo MIF" ),
1781 QStringLiteral( "*.mif" ),
1782 QStringLiteral( "mif" ),
1783 datasetOptions,
1784 layerOptions
1785 )
1786 );
1787
1788 // Microstation DGN
1789 datasetOptions.clear();
1790 layerOptions.clear();
1791
1792 datasetOptions.insert( QStringLiteral( "3D" ), new QgsVectorFileWriter::BoolOption(
1793 QObject::tr( "Determine whether 2D (seed_2d.dgn) or 3D (seed_3d.dgn) "
1794 "seed file should be used. This option is ignored if the SEED option is provided." ),
1795 false // Default value
1796 ) );
1797
1798 datasetOptions.insert( QStringLiteral( "SEED" ), new QgsVectorFileWriter::StringOption(
1799 QObject::tr( "Override the seed file to use." ),
1800 QString() // Default value
1801 ) );
1802
1803 datasetOptions.insert( QStringLiteral( "COPY_WHOLE_SEED_FILE" ), new QgsVectorFileWriter::BoolOption(
1804 QObject::tr( "Indicate whether the whole seed file should be copied. "
1805 "If not, only the first three elements will be copied." ),
1806 false // Default value
1807 ) );
1808
1809 datasetOptions.insert( QStringLiteral( "COPY_SEED_FILE_COLOR_TABLE" ), new QgsVectorFileWriter::BoolOption(
1810 QObject::tr( "Indicates whether the color table should be copied from the seed file." ),
1811 false // Default value
1812 ) );
1813
1814 datasetOptions.insert( QStringLiteral( "MASTER_UNIT_NAME" ), new QgsVectorFileWriter::StringOption(
1815 QObject::tr( "Override the master unit name from the seed file with "
1816 "the provided one or two character unit name." ),
1817 QString() // Default value
1818 ) );
1819
1820 datasetOptions.insert( QStringLiteral( "SUB_UNIT_NAME" ), new QgsVectorFileWriter::StringOption(
1821 QObject::tr( "Override the sub unit name from the seed file with the provided "
1822 "one or two character unit name." ),
1823 QString() // Default value
1824 ) );
1825
1826 datasetOptions.insert( QStringLiteral( "SUB_UNITS_PER_MASTER_UNIT" ), new QgsVectorFileWriter::IntOption(
1827 QObject::tr( "Override the number of subunits per master unit. "
1828 "By default the seed file value is used." ),
1829 0 // Default value
1830 ) );
1831
1832 datasetOptions.insert( QStringLiteral( "UOR_PER_SUB_UNIT" ), new QgsVectorFileWriter::IntOption(
1833 QObject::tr( "Override the number of UORs (Units of Resolution) "
1834 "per sub unit. By default the seed file value is used." ),
1835 0 // Default value
1836 ) );
1837
1838 datasetOptions.insert( QStringLiteral( "ORIGIN" ), new QgsVectorFileWriter::StringOption(
1839 QObject::tr( "ORIGIN=x,y,z: Override the origin of the design plane. "
1840 "By default the origin from the seed file is used." ),
1841 QString() // Default value
1842 ) );
1843
1844 driverMetadata.insert( QStringLiteral( "DGN" ),
1846 QStringLiteral( "Microstation DGN" ),
1847 QObject::tr( "Microstation DGN" ),
1848 QStringLiteral( "*.dgn" ),
1849 QStringLiteral( "dgn" ),
1850 datasetOptions,
1851 layerOptions
1852 )
1853 );
1854
1855 // S-57 Base file
1856 datasetOptions.clear();
1857 layerOptions.clear();
1858
1859 datasetOptions.insert( QStringLiteral( "UPDATES" ), new QgsVectorFileWriter::SetOption(
1860 QObject::tr( "Should update files be incorporated into the base data on the fly." ),
1861 QStringList()
1862 << QStringLiteral( "APPLY" )
1863 << QStringLiteral( "IGNORE" ),
1864 QStringLiteral( "APPLY" ) // Default value
1865 ) );
1866
1867 datasetOptions.insert( QStringLiteral( "SPLIT_MULTIPOINT" ), new QgsVectorFileWriter::BoolOption(
1868 QObject::tr( "Should multipoint soundings be split into many single point sounding features. "
1869 "Multipoint geometries are not well handled by many formats, "
1870 "so it can be convenient to split single sounding features with many points "
1871 "into many single point features." ),
1872 false // Default value
1873 ) );
1874
1875 datasetOptions.insert( QStringLiteral( "ADD_SOUNDG_DEPTH" ), new QgsVectorFileWriter::BoolOption(
1876 QObject::tr( "Should a DEPTH attribute be added on SOUNDG features and assign the depth "
1877 "of the sounding. This should only be enabled when SPLIT_MULTIPOINT is "
1878 "also enabled." ),
1879 false // Default value
1880 ) );
1881
1882 datasetOptions.insert( QStringLiteral( "RETURN_PRIMITIVES" ), new QgsVectorFileWriter::BoolOption(
1883 QObject::tr( "Should all the low level geometry primitives be returned as special "
1884 "IsolatedNode, ConnectedNode, Edge and Face layers." ),
1885 false // Default value
1886 ) );
1887
1888 datasetOptions.insert( QStringLiteral( "PRESERVE_EMPTY_NUMBERS" ), new QgsVectorFileWriter::BoolOption(
1889 QObject::tr( "If enabled, numeric attributes assigned an empty string as a value will "
1890 "be preserved as a special numeric value. This option should not generally "
1891 "be needed, but may be useful when translated S-57 to S-57 losslessly." ),
1892 false // Default value
1893 ) );
1894
1895 datasetOptions.insert( QStringLiteral( "LNAM_REFS" ), new QgsVectorFileWriter::BoolOption(
1896 QObject::tr( "Should LNAM and LNAM_REFS fields be attached to features capturing "
1897 "the feature to feature relationships in the FFPT group of the S-57 file." ),
1898 true // Default value
1899 ) );
1900
1901 datasetOptions.insert( QStringLiteral( "RETURN_LINKAGES" ), new QgsVectorFileWriter::BoolOption(
1902 QObject::tr( "Should additional attributes relating features to their underlying "
1903 "geometric primitives be attached. These are the values of the FSPT group, "
1904 "and are primarily needed when doing S-57 to S-57 translations." ),
1905 false // Default value
1906 ) );
1907
1908 datasetOptions.insert( QStringLiteral( "RECODE_BY_DSSI" ), new QgsVectorFileWriter::BoolOption(
1909 QObject::tr( "Should attribute values be recoded to UTF-8 from the character encoding "
1910 "specified in the S57 DSSI record." ),
1911 false // Default value
1912 ) );
1913
1914 // set OGR_S57_OPTIONS = "RETURN_PRIMITIVES=ON,RETURN_LINKAGES=ON,LNAM_REFS=ON"
1915
1916 driverMetadata.insert( QStringLiteral( "S57" ),
1918 QStringLiteral( "S-57 Base file" ),
1919 QObject::tr( "S-57 Base file" ),
1920 QStringLiteral( "*.000" ),
1921 QStringLiteral( "000" ),
1922 datasetOptions,
1923 layerOptions
1924 )
1925 );
1926
1927 // Spatial Data Transfer Standard [SDTS]
1928 datasetOptions.clear();
1929 layerOptions.clear();
1930
1931 driverMetadata.insert( QStringLiteral( "SDTS" ),
1933 QStringLiteral( "Spatial Data Transfer Standard [SDTS]" ),
1934 QObject::tr( "Spatial Data Transfer Standard [SDTS]" ),
1935 QStringLiteral( "*catd.ddf" ),
1936 QStringLiteral( "ddf" ),
1937 datasetOptions,
1938 layerOptions
1939 )
1940 );
1941
1942 // SQLite
1943 datasetOptions.clear();
1944 layerOptions.clear();
1945
1946 datasetOptions.insert( QStringLiteral( "METADATA" ), new QgsVectorFileWriter::BoolOption(
1947 QObject::tr( "Can be used to avoid creating the geometry_columns and spatial_ref_sys "
1948 "tables in a new database. By default these metadata tables are created "
1949 "when a new database is created." ),
1950 true // Default value
1951 ) );
1952
1953 // Will handle the SpatiaLite alias
1954 datasetOptions.insert( QStringLiteral( "SPATIALITE" ), new QgsVectorFileWriter::HiddenOption(
1955 QStringLiteral( "NO" )
1956 ) );
1957
1958
1959 datasetOptions.insert( QStringLiteral( "INIT_WITH_EPSG" ), new QgsVectorFileWriter::HiddenOption(
1960 QStringLiteral( "NO" )
1961 ) );
1962
1963 layerOptions.insert( QStringLiteral( "FORMAT" ), new QgsVectorFileWriter::SetOption(
1964 QObject::tr( "Controls the format used for the geometry column. Defaults to WKB. "
1965 "This is generally more space and processing efficient, but harder "
1966 "to inspect or use in simple applications than WKT (Well Known Text)." ),
1967 QStringList()
1968 << QStringLiteral( "WKB" )
1969 << QStringLiteral( "WKT" ),
1970 QStringLiteral( "WKB" ) // Default value
1971 ) );
1972
1973 layerOptions.insert( QStringLiteral( "LAUNDER" ), new QgsVectorFileWriter::BoolOption(
1974 QObject::tr( "Controls whether layer and field names will be laundered for easier use "
1975 "in SQLite. Laundered names will be converted to lower case and some special "
1976 "characters(' - #) will be changed to underscores." ),
1977 true // Default value
1978 ) );
1979
1980 layerOptions.insert( QStringLiteral( "SPATIAL_INDEX" ), new QgsVectorFileWriter::HiddenOption(
1981 QStringLiteral( "NO" )
1982 ) );
1983
1984 layerOptions.insert( QStringLiteral( "COMPRESS_GEOM" ), new QgsVectorFileWriter::HiddenOption(
1985 QStringLiteral( "NO" )
1986 ) );
1987
1988 layerOptions.insert( QStringLiteral( "SRID" ), new QgsVectorFileWriter::HiddenOption(
1989 QString()
1990 ) );
1991
1992 layerOptions.insert( QStringLiteral( "COMPRESS_COLUMNS" ), new QgsVectorFileWriter::StringOption(
1993 QObject::tr( "column_name1[,column_name2, …] A list of (String) columns that "
1994 "must be compressed with ZLib DEFLATE algorithm. This might be beneficial "
1995 "for databases that have big string blobs. However, use with care, since "
1996 "the value of such columns will be seen as compressed binary content with "
1997 "other SQLite utilities (or previous OGR versions). With OGR, when inserting, "
1998 "modifying or querying compressed columns, compression/decompression is "
1999 "done transparently. However, such columns cannot be (easily) queried with "
2000 "an attribute filter or WHERE clause. Note: in table definition, such columns "
2001 "have the 'VARCHAR_deflate' declaration type." ),
2002 QString() // Default value
2003 ) );
2004
2005 driverMetadata.insert( QStringLiteral( "SQLite" ),
2007 QStringLiteral( "SQLite" ),
2008 QObject::tr( "SQLite" ),
2009 QStringLiteral( "*.sqlite" ),
2010 QStringLiteral( "sqlite" ),
2011 datasetOptions,
2012 layerOptions,
2013 QStringLiteral( "UTF-8" )
2014 )
2015 );
2016
2017 // SpatiaLite
2018 datasetOptions.clear();
2019 layerOptions.clear();
2020
2021 datasetOptions.insert( QStringLiteral( "METADATA" ), new QgsVectorFileWriter::BoolOption(
2022 QObject::tr( "Can be used to avoid creating the geometry_columns and spatial_ref_sys "
2023 "tables in a new database. By default these metadata tables are created "
2024 "when a new database is created." ),
2025 true // Default value
2026 ) );
2027
2028 datasetOptions.insert( QStringLiteral( "SPATIALITE" ), new QgsVectorFileWriter::HiddenOption(
2029 QStringLiteral( "YES" )
2030 ) );
2031
2032 datasetOptions.insert( QStringLiteral( "INIT_WITH_EPSG" ), new QgsVectorFileWriter::BoolOption(
2033 QObject::tr( "Insert the content of the EPSG CSV files into the spatial_ref_sys table. "
2034 "Set to NO for regular SQLite databases." ),
2035 true // Default value
2036 ) );
2037
2038 layerOptions.insert( QStringLiteral( "FORMAT" ), new QgsVectorFileWriter::HiddenOption(
2039 QStringLiteral( "SPATIALITE" )
2040 ) );
2041
2042 layerOptions.insert( QStringLiteral( "LAUNDER" ), new QgsVectorFileWriter::BoolOption(
2043 QObject::tr( "Controls whether layer and field names will be laundered for easier use "
2044 "in SQLite. Laundered names will be converted to lower case and some special "
2045 "characters(' - #) will be changed to underscores." ),
2046 true // Default value
2047 ) );
2048
2049 layerOptions.insert( QStringLiteral( "SPATIAL_INDEX" ), new QgsVectorFileWriter::BoolOption(
2050 QObject::tr( "If the database is of the SpatiaLite flavor, and if OGR is linked "
2051 "against libspatialite, this option can be used to control if a spatial "
2052 "index must be created." ),
2053 true // Default value
2054 ) );
2055
2056 layerOptions.insert( QStringLiteral( "COMPRESS_GEOM" ), new QgsVectorFileWriter::BoolOption(
2057 QObject::tr( "If the format of the geometry BLOB is of the SpatiaLite flavor, "
2058 "this option can be used to control if the compressed format for "
2059 "geometries (LINESTRINGs, POLYGONs) must be used." ),
2060 false // Default value
2061 ) );
2062
2063 layerOptions.insert( QStringLiteral( "SRID" ), new QgsVectorFileWriter::StringOption(
2064 QObject::tr( "Used to force the SRID number of the SRS associated with the layer. "
2065 "When this option isn't specified and that a SRS is associated with the "
2066 "layer, a search is made in the spatial_ref_sys to find a match for the "
2067 "SRS, and, if there is no match, a new entry is inserted for the SRS in "
2068 "the spatial_ref_sys table. When the SRID option is specified, this "
2069 "search (and the eventual insertion of a new entry) will not be done: "
2070 "the specified SRID is used as such." ),
2071 QString() // Default value
2072 ) );
2073
2074 layerOptions.insert( QStringLiteral( "COMPRESS_COLUMNS" ), new QgsVectorFileWriter::StringOption(
2075 QObject::tr( "column_name1[,column_name2, …] A list of (String) columns that "
2076 "must be compressed with ZLib DEFLATE algorithm. This might be beneficial "
2077 "for databases that have big string blobs. However, use with care, since "
2078 "the value of such columns will be seen as compressed binary content with "
2079 "other SQLite utilities (or previous OGR versions). With OGR, when inserting, "
2080 "modifying or queryings compressed columns, compression/decompression is "
2081 "done transparently. However, such columns cannot be (easily) queried with "
2082 "an attribute filter or WHERE clause. Note: in table definition, such columns "
2083 "have the 'VARCHAR_deflate' declaration type." ),
2084 QString() // Default value
2085 ) );
2086
2087 driverMetadata.insert( QStringLiteral( "SpatiaLite" ),
2089 QStringLiteral( "SpatiaLite" ),
2090 QObject::tr( "SpatiaLite" ),
2091 QStringLiteral( "*.sqlite" ),
2092 QStringLiteral( "sqlite" ),
2093 datasetOptions,
2094 layerOptions,
2095 QStringLiteral( "UTF-8" )
2096 )
2097 );
2098 // AutoCAD DXF
2099 datasetOptions.clear();
2100 layerOptions.clear();
2101
2102 datasetOptions.insert( QStringLiteral( "HEADER" ), new QgsVectorFileWriter::StringOption(
2103 QObject::tr( "Override the header file used - in place of header.dxf." ),
2104 QString() // Default value
2105 ) );
2106
2107 datasetOptions.insert( QStringLiteral( "TRAILER" ), new QgsVectorFileWriter::StringOption(
2108 QObject::tr( "Override the trailer file used - in place of trailer.dxf." ),
2109 QString() // Default value
2110 ) );
2111
2112 driverMetadata.insert( QStringLiteral( "DXF" ),
2114 QStringLiteral( "AutoCAD DXF" ),
2115 QObject::tr( "AutoCAD DXF" ),
2116 QStringLiteral( "*.dxf" ),
2117 QStringLiteral( "dxf" ),
2118 datasetOptions,
2119 layerOptions
2120 )
2121 );
2122
2123 // Geoconcept
2124 datasetOptions.clear();
2125 layerOptions.clear();
2126
2127 datasetOptions.insert( QStringLiteral( "EXTENSION" ), new QgsVectorFileWriter::SetOption(
2128 QObject::tr( "Indicates the GeoConcept export file extension. "
2129 "TXT was used by earlier releases of GeoConcept. GXT is currently used." ),
2130 QStringList()
2131 << QStringLiteral( "GXT" )
2132 << QStringLiteral( "TXT" ),
2133 QStringLiteral( "GXT" ) // Default value
2134 ) );
2135
2136 datasetOptions.insert( QStringLiteral( "CONFIG" ), new QgsVectorFileWriter::StringOption(
2137 QObject::tr( "Path to the GCT: the GCT file describes the GeoConcept types definitions: "
2138 "In this file, every line must start with //# followed by a keyword. "
2139 "Lines starting with // are comments." ),
2140 QString() // Default value
2141 ) );
2142
2143 datasetOptions.insert( QStringLiteral( "FEATURETYPE" ), new QgsVectorFileWriter::StringOption(
2144 QObject::tr( "Defines the feature to be created. The TYPE corresponds to one of the Name "
2145 "found in the GCT file for a type section. The SUBTYPE corresponds to one of "
2146 "the Name found in the GCT file for a sub-type section within the previous "
2147 "type section." ),
2148 QString() // Default value
2149 ) );
2150
2151 driverMetadata.insert( QStringLiteral( "Geoconcept" ),
2153 QStringLiteral( "Geoconcept" ),
2154 QObject::tr( "Geoconcept" ),
2155 QStringLiteral( "*.gxt *.txt" ),
2156 QStringLiteral( "gxt" ),
2157 datasetOptions,
2158 layerOptions
2159 )
2160 );
2161
2162 // ESRI OpenFileGDB
2163 datasetOptions.clear();
2164 layerOptions.clear();
2165
2166#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,9,0)
2167 layerOptions.insert( QStringLiteral( "TARGET_ARCGIS_VERSION" ), new QgsVectorFileWriter::SetOption(
2168 QObject::tr( "Selects which ArcGIS version this dataset should be compatible with. ALL is used by default and means any ArcGIS 10.x or ArcGIS Pro version. Using ARCGIS_PRO_3_2_OR_LATER is required to export 64-bit integer fields as such, otherwise they will be converted as Real fields. ARCGIS_PRO_3_2_OR_LATER also supports proper Date and Time field types." ),
2169 QStringList()
2170 << QStringLiteral( "ALL" )
2171 << QStringLiteral( "ARCGIS_PRO_3_2_OR_LATER" ),
2172 QStringLiteral( "ALL" ) // Default value
2173 ) );
2174#endif
2175
2176 layerOptions.insert( QStringLiteral( "FEATURE_DATASET" ), new QgsVectorFileWriter::StringOption(
2177 QObject::tr( "When this option is set, the new layer will be created inside the named "
2178 "FeatureDataset folder. If the folder does not already exist, it will be created." ),
2179 QString() // Default value
2180 ) );
2181
2182 layerOptions.insert( QStringLiteral( "LAYER_ALIAS" ), new QgsVectorFileWriter::StringOption(
2183 QObject::tr( "Set layer name alias." ),
2184 QString() // Default value
2185 ) );
2186
2187 layerOptions.insert( QStringLiteral( "GEOMETRY_NAME" ), new QgsVectorFileWriter::StringOption(
2188 QObject::tr( "Set name of geometry column in new layer. Defaults to 'SHAPE'." ),
2189 QStringLiteral( "SHAPE" ) // Default value
2190 ) );
2191
2192 layerOptions.insert( QStringLiteral( "GEOMETRY_NULLABLE" ), new QgsVectorFileWriter::BoolOption(
2193 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'." ),
2194 true // Default value
2195 ) );
2196
2197 layerOptions.insert( QStringLiteral( "FID" ), new QgsVectorFileWriter::StringOption(
2198 QObject::tr( "Name of the OID column to create. Defaults to 'OBJECTID'." ),
2199 QStringLiteral( "OBJECTID" ) // Default value
2200 ) );
2201
2202 // TODO missing options -- requires double option type
2203 // XYTOLERANCE
2204 // ZTOLERANCE
2205 // MTOLERANCE
2206 // XORIGIN
2207 // YORIGIN
2208 // ZORIGIN
2209 // MORIGIN
2210 // XYSCALE
2211 // ZSCALE
2212 // ZORIGIN
2213
2214 layerOptions.insert( QStringLiteral( "COLUMN_TYPES" ), new QgsVectorFileWriter::StringOption(
2215 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." ),
2216 QString( ) // Default value
2217 ) );
2218
2219 layerOptions.insert( QStringLiteral( "DOCUMENTATION" ), new QgsVectorFileWriter::StringOption(
2220 QObject::tr( "XML documentation for the layer." ),
2221 QString( ) // Default value
2222 ) );
2223 layerOptions.insert( QStringLiteral( "CONFIGURATION_KEYWORD" ), new QgsVectorFileWriter::SetOption(
2224 QObject::tr( "Customize how data is stored. By default text in UTF-8 and data up to 1TB." ),
2225 {QStringLiteral( "DEFAULTS" ), QStringLiteral( "MAX_FILE_SIZE_4GB" ), QStringLiteral( "MAX_FILE_SIZE_256TB" )},
2226 QStringLiteral( "DEFAULTS" ), // Default value
2227 false // Allow None
2228 ) );
2229
2230 layerOptions.insert( QStringLiteral( "CREATE_SHAPE_AREA_AND_LENGTH_FIELDS" ), new QgsVectorFileWriter::BoolOption(
2231 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." ),
2232 false // Default value
2233 ) );
2234
2235 driverMetadata.insert( QStringLiteral( "OpenFileGDB" ),
2237 QStringLiteral( "ESRI File Geodatabase" ),
2238 QObject::tr( "ESRI File Geodatabase" ),
2239 QStringLiteral( "*.gdb" ),
2240 QStringLiteral( "gdb" ),
2241 datasetOptions,
2242 layerOptions,
2243 QStringLiteral( "UTF-8" )
2244 )
2245 );
2246
2247 // ESRI FileGDB (using ESRI FileGDB API SDK)
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( "GEOMETRY_NAME" ), new QgsVectorFileWriter::StringOption(
2258 QObject::tr( "Set name of geometry column in new layer. Defaults to 'SHAPE'." ),
2259 QStringLiteral( "SHAPE" ) // Default value
2260 ) );
2261
2262 layerOptions.insert( QStringLiteral( "FID" ), new QgsVectorFileWriter::StringOption(
2263 QObject::tr( "Name of the OID column to create. Defaults to 'OBJECTID'." ),
2264 QStringLiteral( "OBJECTID" ) // Default value
2265 ) );
2266
2267 driverMetadata.insert( QStringLiteral( "FileGDB" ),
2269 QStringLiteral( "ESRI FileGDB" ),
2270 QObject::tr( "ESRI FileGDB" ),
2271 QStringLiteral( "*.gdb" ),
2272 QStringLiteral( "gdb" ),
2273 datasetOptions,
2274 layerOptions,
2275 QStringLiteral( "UTF-8" )
2276 )
2277 );
2278
2279 // XLSX
2280 datasetOptions.clear();
2281 layerOptions.clear();
2282
2283 layerOptions.insert( QStringLiteral( "OGR_XLSX_FIELD_TYPES" ), new QgsVectorFileWriter::SetOption(
2284 QObject::tr( "By default, the driver will try to detect the data type of fields. If set "
2285 "to STRING, all fields will be of String type." ),
2286 QStringList()
2287 << QStringLiteral( "AUTO" )
2288 << QStringLiteral( "STRING" ),
2289 QStringLiteral( "AUTO" ), // Default value
2290 false // Allow None
2291 ) );
2292
2293 layerOptions.insert( QStringLiteral( "OGR_XLSX_HEADERS" ), new QgsVectorFileWriter::SetOption(
2294 QObject::tr( "By default, the driver will read the first lines of each sheet to detect "
2295 "if the first line might be the name of columns. If set to FORCE, the driver "
2296 "will consider the first line as the header line. If set to "
2297 "DISABLE, it will be considered as the first feature. Otherwise "
2298 "auto-detection will occur." ),
2299 QStringList()
2300 << QStringLiteral( "FORCE" )
2301 << QStringLiteral( "DISABLE" )
2302 << QStringLiteral( "AUTO" ),
2303 QStringLiteral( "AUTO" ), // Default value
2304 false // Allow None
2305 ) );
2306
2307 driverMetadata.insert( QStringLiteral( "XLSX" ),
2309 QStringLiteral( "MS Office Open XML spreadsheet" ),
2310 QObject::tr( "MS Office Open XML spreadsheet [XLSX]" ),
2311 QStringLiteral( "*.xlsx" ),
2312 QStringLiteral( "xlsx" ),
2313 datasetOptions,
2314 layerOptions,
2315 QStringLiteral( "UTF-8" )
2316 )
2317 );
2318
2319 // ODS
2320 datasetOptions.clear();
2321 layerOptions.clear();
2322
2323 layerOptions.insert( QStringLiteral( "OGR_ODS_FIELD_TYPES" ), new QgsVectorFileWriter::SetOption(
2324 QObject::tr( "By default, the driver will try to detect the data type of fields. If set "
2325 "to STRING, all fields will be of String type." ),
2326 QStringList()
2327 << QStringLiteral( "AUTO" )
2328 << QStringLiteral( "STRING" ),
2329 QStringLiteral( "AUTO" ), // Default value
2330 false // Allow None
2331 ) );
2332
2333 layerOptions.insert( QStringLiteral( "OGR_ODS_HEADERS" ), new QgsVectorFileWriter::SetOption(
2334 QObject::tr( "By default, the driver will read the first lines of each sheet to detect "
2335 "if the first line might be the name of columns. If set to FORCE, the driver "
2336 "will consider the first line as the header line. If set to "
2337 "DISABLE, it will be considered as the first feature. Otherwise "
2338 "auto-detection will occur." ),
2339 QStringList()
2340 << QStringLiteral( "FORCE" )
2341 << QStringLiteral( "DISABLE" )
2342 << QStringLiteral( "AUTO" ),
2343 QStringLiteral( "AUTO" ), // Default value
2344 false // Allow None
2345 ) );
2346
2347 driverMetadata.insert( QStringLiteral( "ODS" ),
2349 QStringLiteral( "Open Document Spreadsheet" ),
2350 QObject::tr( "Open Document Spreadsheet [ODS]" ),
2351 QStringLiteral( "*.ods" ),
2352 QStringLiteral( "ods" ),
2353 datasetOptions,
2354 layerOptions,
2355 QStringLiteral( "UTF-8" )
2356 )
2357 );
2358
2359 // Parquet
2360 datasetOptions.clear();
2361 layerOptions.clear();
2362
2363 layerOptions.insert( QStringLiteral( "COMPRESSION" ), new QgsVectorFileWriter::SetOption(
2364 QObject::tr( "Compression method." ),
2365 QStringList()
2366 << QStringLiteral( "UNCOMPRESSED" )
2367 << QStringLiteral( "SNAPPY" ),
2368 QStringLiteral( "SNAPPY" ), // Default value
2369 false // Allow None
2370 ) );
2371
2372 layerOptions.insert( QStringLiteral( "GEOMETRY_ENCODING" ), new QgsVectorFileWriter::SetOption(
2373 QObject::tr( "Geometry encoding." ),
2374 QStringList()
2375 << QStringLiteral( "WKB" )
2376 << QStringLiteral( "WKT" )
2377 << QStringLiteral( "GEOARROW" ),
2378 QStringLiteral( "WKB" ), // Default value
2379 false // Allow None
2380 ) );
2381
2382 layerOptions.insert( QStringLiteral( "ROW_GROUP_SIZE" ), new QgsVectorFileWriter::IntOption(
2383 QObject::tr( "Maximum number of rows per group." ),
2384 65536 // Default value
2385 ) );
2386
2387 layerOptions.insert( QStringLiteral( "FID" ), new QgsVectorFileWriter::StringOption(
2388 QObject::tr( "Name for the feature identifier column" ),
2389 QString() // Default value
2390 ) );
2391
2392 layerOptions.insert( QStringLiteral( "GEOMETRY_NAME" ), new QgsVectorFileWriter::StringOption(
2393 QObject::tr( "Name for the geometry column" ),
2394 QStringLiteral( "geometry" ) // Default value
2395 ) );
2396
2397 layerOptions.insert( QStringLiteral( "EDGES" ), new QgsVectorFileWriter::SetOption(
2398 QObject::tr( "Name of the coordinate system for the edges." ),
2399 QStringList()
2400 << QStringLiteral( "PLANAR" )
2401 << QStringLiteral( "SPHERICAL" ),
2402 QStringLiteral( "PLANAR" ), // Default value
2403 false // Allow None
2404 ) );
2405
2406 driverMetadata.insert( QStringLiteral( "Parquet" ),
2408 QStringLiteral( "(Geo)Parquet" ),
2409 QObject::tr( "(Geo)Parquet" ),
2410 QStringLiteral( "*.parquet" ),
2411 QStringLiteral( "parquet" ),
2412 datasetOptions,
2413 layerOptions,
2414 QStringLiteral( "UTF-8" )
2415 )
2416 );
2417
2418 // PGDump
2419 datasetOptions.clear();
2420 layerOptions.clear();
2421
2422 datasetOptions.insert( QStringLiteral( "LINEFORMAT" ), new QgsVectorFileWriter::SetOption(
2423 QObject::tr( "Line termination character sequence." ),
2424 QStringList()
2425 << QStringLiteral( "CRLF" )
2426 << QStringLiteral( "LF" ),
2427 QStringLiteral( "LF" ), // Default value
2428 false // Allow None
2429 ) );
2430
2431
2432 layerOptions.insert( QStringLiteral( "GEOM_TYPE" ), new QgsVectorFileWriter::SetOption(
2433 QObject::tr( "Format of geometry columns." ),
2434 QStringList()
2435 << QStringLiteral( "geometry" )
2436 << QStringLiteral( "geography" ),
2437 QStringLiteral( "geometry" ), // Default value
2438 false // Allow None
2439 ) );
2440
2441 layerOptions.insert( QStringLiteral( "LAUNDER" ), new QgsVectorFileWriter::BoolOption(
2442 QObject::tr( "Controls whether layer and field names will be laundered for easier use. "
2443 "Laundered names will be converted to lower case and some special "
2444 "characters(' - #) will be changed to underscores." ),
2445 true // Default value
2446 ) );
2447
2448 layerOptions.insert( QStringLiteral( "GEOMETRY_NAME" ), new QgsVectorFileWriter::StringOption(
2449 QObject::tr( "Name for the geometry column. Defaults to wkb_geometry "
2450 "for GEOM_TYPE=geometry or the_geog for GEOM_TYPE=geography" ) ) );
2451
2452 layerOptions.insert( QStringLiteral( "SCHEMA" ), new QgsVectorFileWriter::StringOption(
2453 QObject::tr( "Name of schema into which to create the new table" ) ) );
2454
2455 layerOptions.insert( QStringLiteral( "CREATE_SCHEMA" ), new QgsVectorFileWriter::BoolOption(
2456 QObject::tr( "Whether to explicitly emit the CREATE SCHEMA statement to create the specified schema." ),
2457 true // Default value
2458 ) );
2459
2460 layerOptions.insert( QStringLiteral( "CREATE_TABLE" ), new QgsVectorFileWriter::BoolOption(
2461 QObject::tr( "Whether to explicitly recreate the table if necessary." ),
2462 true // Default value
2463 ) );
2464
2465 layerOptions.insert( QStringLiteral( "DROP_TABLE" ), new QgsVectorFileWriter::SetOption(
2466 QObject::tr( "Whether to explicitly destroy tables before recreating them." ),
2467 QStringList()
2468 << QStringLiteral( "YES" )
2469 << QStringLiteral( "NO" )
2470 << QStringLiteral( "IF_EXISTS" ),
2471 QStringLiteral( "YES" ), // Default value
2472 false // Allow None
2473 ) );
2474
2475 layerOptions.insert( QStringLiteral( "SRID" ), new QgsVectorFileWriter::StringOption(
2476 QObject::tr( "Used to force the SRID number of the SRS associated with the layer. "
2477 "When this option isn't specified and that a SRS is associated with the "
2478 "layer, a search is made in the spatial_ref_sys to find a match for the "
2479 "SRS, and, if there is no match, a new entry is inserted for the SRS in "
2480 "the spatial_ref_sys table. When the SRID option is specified, this "
2481 "search (and the eventual insertion of a new entry) will not be done: "
2482 "the specified SRID is used as such." ),
2483 QString() // Default value
2484 ) );
2485
2486 layerOptions.insert( QStringLiteral( "POSTGIS_VERSION" ), new QgsVectorFileWriter::StringOption(
2487 QObject::tr( "Can be set to 2.0 or 2.2 for PostGIS 2.0/2.2 compatibility. "
2488 "Important to set it correctly if using non-linear geometry types" ),
2489 QStringLiteral( "2.2" ) // Default value
2490 ) );
2491
2492 driverMetadata.insert( QStringLiteral( "PGDUMP" ),
2494 QStringLiteral( "PostgreSQL SQL dump" ),
2495 QObject::tr( "PostgreSQL SQL dump" ),
2496 QStringLiteral( "*.sql" ),
2497 QStringLiteral( "sql" ),
2498 datasetOptions,
2499 layerOptions,
2500 QStringLiteral( "UTF-8" )
2501 )
2502 );
2503
2504 }
2505
2506 QgsVectorFileWriterMetadataContainer( const QgsVectorFileWriterMetadataContainer &other ) = delete;
2507 QgsVectorFileWriterMetadataContainer &operator=( const QgsVectorFileWriterMetadataContainer &other ) = delete;
2508 ~QgsVectorFileWriterMetadataContainer()
2509 {
2510 for ( auto it = driverMetadata.constBegin(); it != driverMetadata.constEnd(); ++it )
2511 {
2512 for ( auto optionIt = it.value().driverOptions.constBegin(); optionIt != it.value().driverOptions.constEnd(); ++optionIt )
2513 delete optionIt.value();
2514 for ( auto optionIt = it.value().layerOptions.constBegin(); optionIt != it.value().layerOptions.constEnd(); ++optionIt )
2515 delete optionIt.value();
2516 }
2517 }
2518
2519 QMap<QString, QgsVectorFileWriter::MetaData> driverMetadata;
2520
2521};
2523
2524bool QgsVectorFileWriter::driverMetadata( const QString &driverName, QgsVectorFileWriter::MetaData &driverMetadata )
2525{
2526 static QgsVectorFileWriterMetadataContainer sDriverMetadata;
2527 QMap<QString, MetaData>::ConstIterator it = sDriverMetadata.driverMetadata.constBegin();
2528
2529 for ( ; it != sDriverMetadata.driverMetadata.constEnd(); ++it )
2530 {
2531 if ( it.key() == QLatin1String( "PGDUMP" ) &&
2532 driverName != QLatin1String( "PGDUMP" ) &&
2533 driverName != QLatin1String( "PostgreSQL SQL dump" ) )
2534 {
2535 // We do not want the 'PG' driver to be wrongly identified with PGDUMP
2536 continue;
2537 }
2538 if ( it.key().startsWith( driverName ) || it.value().longName.startsWith( driverName ) )
2539 {
2540 driverMetadata = it.value();
2541 return true;
2542 }
2543 }
2544
2545 return false;
2546}
2547
2548QStringList QgsVectorFileWriter::defaultDatasetOptions( const QString &driverName )
2549{
2550 MetaData metadata;
2551 bool ok = driverMetadata( driverName, metadata );
2552 if ( !ok )
2553 return QStringList();
2554 return concatenateOptions( metadata.driverOptions );
2555}
2556
2557QStringList QgsVectorFileWriter::defaultLayerOptions( const QString &driverName )
2558{
2559 MetaData metadata;
2560 bool ok = driverMetadata( driverName, metadata );
2561 if ( !ok )
2562 return QStringList();
2563 return concatenateOptions( metadata.layerOptions );
2564}
2565
2567{
2568
2569 OGRwkbGeometryType ogrType = static_cast<OGRwkbGeometryType>( type );
2570
2572 {
2573 ogrType = static_cast<OGRwkbGeometryType>( QgsWkbTypes::to25D( type ) );
2574 }
2575 return ogrType;
2576}
2577
2582
2584{
2585 return mErrorMessage;
2586}
2587
2589{
2590 return mOgrDriverName;
2591}
2592
2594{
2595 return mOgrDriverLongName;
2596}
2597
2599{
2600 return mCapabilities;
2601}
2602
2607
2609{
2610 QgsFeatureList::iterator fIt = features.begin();
2611 bool result = true;
2612 for ( ; fIt != features.end(); ++fIt )
2613 {
2614 result = result && addFeatureWithStyle( *fIt, nullptr, Qgis::DistanceUnit::Meters );
2615 }
2616 return result;
2617}
2618
2620{
2621 return mErrorMessage;
2622}
2623
2625{
2626 // create the feature
2627 gdal::ogr_feature_unique_ptr poFeature = createFeature( feature );
2628 if ( !poFeature )
2629 return false;
2630
2631 //add OGR feature style type
2633 {
2634 mRenderContext.expressionContext().setFeature( feature );
2635 //SymbolLayerSymbology: concatenate ogr styles of all symbollayers
2636 QgsSymbolList symbols = renderer->symbolsForFeature( feature, mRenderContext );
2637 QString styleString;
2638 QString currentStyle;
2639
2640 QgsSymbolList::const_iterator symbolIt = symbols.constBegin();
2641 for ( ; symbolIt != symbols.constEnd(); ++symbolIt )
2642 {
2643 int nSymbolLayers = ( *symbolIt )->symbolLayerCount();
2644 for ( int i = 0; i < nSymbolLayers; ++i )
2645 {
2646#if 0
2647 QMap< QgsSymbolLayer *, QString >::const_iterator it = mSymbolLayerTable.find( ( *symbolIt )->symbolLayer( i ) );
2648 if ( it == mSymbolLayerTable.constEnd() )
2649 {
2650 continue;
2651 }
2652#endif
2653 double mmsf = mmScaleFactor( mSymbologyScale, ( *symbolIt )->outputUnit(), outputUnit );
2654 double musf = mapUnitScaleFactor( mSymbologyScale, ( *symbolIt )->outputUnit(), outputUnit );
2655
2656 currentStyle = ( *symbolIt )->symbolLayer( i )->ogrFeatureStyle( mmsf, musf );//"@" + it.value();
2657
2658 switch ( mSymbologyExport )
2659 {
2661 {
2662 if ( symbolIt != symbols.constBegin() || i != 0 )
2663 {
2664 styleString.append( ';' );
2665 }
2666 styleString.append( currentStyle );
2667 break;
2668 }
2670 {
2671 OGR_F_SetStyleString( poFeature.get(), currentStyle.toLocal8Bit().constData() );
2672 if ( !writeFeature( mLayer, poFeature.get() ) )
2673 {
2674 return false;
2675 }
2676 break;
2677 }
2678
2680 break;
2681 }
2682 }
2683 }
2684 OGR_F_SetStyleString( poFeature.get(), styleString.toLocal8Bit().constData() );
2685 }
2686
2687 switch ( mSymbologyExport )
2688 {
2691 {
2692 if ( !writeFeature( mLayer, poFeature.get() ) )
2693 {
2694 return false;
2695 }
2696 break;
2697 }
2698
2700 break;
2701 }
2702
2703 return true;
2704}
2705
2706gdal::ogr_feature_unique_ptr QgsVectorFileWriter::createFeature( const QgsFeature &feature )
2707{
2708 QgsLocaleNumC l; // Make sure the decimal delimiter is a dot
2709 Q_UNUSED( l )
2710
2711 gdal::ogr_feature_unique_ptr poFeature( OGR_F_Create( OGR_L_GetLayerDefn( mLayer ) ) );
2712
2713 // attribute handling
2714 for ( QMap<int, int>::const_iterator it = mAttrIdxToOgrIdx.constBegin(); it != mAttrIdxToOgrIdx.constEnd(); ++it )
2715 {
2716 int fldIdx = it.key();
2717 int ogrField = it.value();
2718
2719 QVariant attrValue = feature.attribute( fldIdx );
2720 QgsField field = mFields.at( fldIdx );
2721
2722 if ( feature.isUnsetValue( fldIdx ) )
2723 {
2724 OGR_F_UnsetField( poFeature.get(), ogrField );
2725 continue;
2726 }
2727 else if ( QgsVariantUtils::isNull( attrValue ) )
2728 {
2729// Starting with GDAL 2.2, there are 2 concepts: unset fields and null fields
2730// whereas previously there was only unset fields. For a GeoJSON output,
2731// leaving a field unset will cause it to not appear at all in the output
2732// feature.
2733// When all features of a layer have a field unset, this would cause the
2734// field to not be present at all in the output, and thus on reading to
2735// have disappeared. #16812
2736#ifdef OGRNullMarker
2737 OGR_F_SetFieldNull( poFeature.get(), ogrField );
2738#endif
2739 continue;
2740 }
2741
2743 {
2744 field = mFieldValueConverter->fieldDefinition( field );
2745 attrValue = mFieldValueConverter->convert( fldIdx, attrValue );
2746 }
2747
2748 // Check type compatibility before passing attribute value to OGR
2749 QString errorMessage;
2750 if ( ! field.convertCompatible( attrValue, &errorMessage ) )
2751 {
2752 mErrorMessage = QObject::tr( "Error converting value (%1) for attribute field %2: %3" )
2753 .arg( feature.attribute( fldIdx ).toString(),
2754 mFields.at( fldIdx ).name(), errorMessage );
2755 QgsMessageLog::logMessage( mErrorMessage, QObject::tr( "OGR" ) );
2757 return nullptr;
2758 }
2759
2760 switch ( field.type() )
2761 {
2762 case QMetaType::Type::Int:
2763 OGR_F_SetFieldInteger( poFeature.get(), ogrField, attrValue.toInt() );
2764 break;
2765 case QMetaType::Type::LongLong:
2766 OGR_F_SetFieldInteger64( poFeature.get(), ogrField, attrValue.toLongLong() );
2767 break;
2768 case QMetaType::Type::Bool:
2769 OGR_F_SetFieldInteger( poFeature.get(), ogrField, attrValue.toInt() );
2770 break;
2771 case QMetaType::Type::QString:
2772 OGR_F_SetFieldString( poFeature.get(), ogrField, mCodec->fromUnicode( attrValue.toString() ).constData() );
2773 break;
2774 case QMetaType::Type::Double:
2775 OGR_F_SetFieldDouble( poFeature.get(), ogrField, attrValue.toDouble() );
2776 break;
2777 case QMetaType::Type::QDate:
2778 OGR_F_SetFieldDateTime( poFeature.get(), ogrField,
2779 attrValue.toDate().year(),
2780 attrValue.toDate().month(),
2781 attrValue.toDate().day(),
2782 0, 0, 0, 0 );
2783 break;
2784 case QMetaType::Type::QDateTime:
2785 if ( mOgrDriverName == QLatin1String( "ESRI Shapefile" ) )
2786 {
2787 OGR_F_SetFieldString( poFeature.get(), ogrField, mCodec->fromUnicode( attrValue.toDateTime().toString( QStringLiteral( "yyyy/MM/dd hh:mm:ss.zzz" ) ) ).constData() );
2788 }
2789 else
2790 {
2791 const QDateTime dt = attrValue.toDateTime();
2792 const QDate date = dt.date();
2793 const QTime time = dt.time();
2794 OGR_F_SetFieldDateTimeEx( poFeature.get(), ogrField,
2795 date.year(),
2796 date.month(),
2797 date.day(),
2798 time.hour(),
2799 time.minute(),
2800 static_cast<float>( time.second() + static_cast< double >( time.msec() ) / 1000 ),
2802 }
2803 break;
2804 case QMetaType::Type::QTime:
2805 if ( mOgrDriverName == QLatin1String( "ESRI Shapefile" ) )
2806 {
2807 OGR_F_SetFieldString( poFeature.get(), ogrField, mCodec->fromUnicode( attrValue.toString() ).constData() );
2808 }
2809 else
2810 {
2811 const QTime time = attrValue.toTime();
2812 OGR_F_SetFieldDateTimeEx( poFeature.get(), ogrField,
2813 0, 0, 0,
2814 time.hour(),
2815 time.minute(),
2816 static_cast<float>( time.second() + static_cast< double >( time.msec() ) / 1000 ),
2817 0 );
2818 }
2819 break;
2820
2821 case QMetaType::Type::QByteArray:
2822 {
2823 const QByteArray ba = attrValue.toByteArray();
2824 OGR_F_SetFieldBinary( poFeature.get(), ogrField, ba.size(), const_cast< GByte * >( reinterpret_cast< const GByte * >( ba.data() ) ) );
2825 break;
2826 }
2827
2828 case QMetaType::Type::UnknownType:
2829 break;
2830
2831 case QMetaType::Type::QStringList:
2832 {
2833 // handle GPKG conversion to JSON
2834 if ( mOgrDriverName == QLatin1String( "GPKG" ) )
2835 {
2836 const QJsonDocument doc = QJsonDocument::fromVariant( attrValue );
2837 QString jsonString;
2838 if ( !doc.isNull() )
2839 {
2840 jsonString = QString::fromUtf8( doc.toJson( QJsonDocument::Compact ).constData() );
2841 }
2842 OGR_F_SetFieldString( poFeature.get(), ogrField, mCodec->fromUnicode( jsonString.constData() ) );
2843 break;
2844 }
2845
2846 QStringList list = attrValue.toStringList();
2847 if ( mSupportedListSubTypes.contains( QMetaType::Type::QString ) )
2848 {
2849 int count = list.count();
2850 char **lst = new char *[count + 1];
2851 if ( count > 0 )
2852 {
2853 int pos = 0;
2854 for ( const QString &string : list )
2855 {
2856 lst[pos] = CPLStrdup( mCodec->fromUnicode( string ).data() );
2857 pos++;
2858 }
2859 }
2860 lst[count] = nullptr;
2861 OGR_F_SetFieldStringList( poFeature.get(), ogrField, lst );
2862 CSLDestroy( lst );
2863 }
2864 else
2865 {
2866 OGR_F_SetFieldString( poFeature.get(), ogrField, mCodec->fromUnicode( list.join( ',' ) ).constData() );
2867 }
2868 break;
2869 }
2870
2871 case QMetaType::Type::QVariantList:
2872 // handle GPKG conversion to JSON
2873 if ( mOgrDriverName == QLatin1String( "GPKG" ) )
2874 {
2875 const QJsonDocument doc = QJsonDocument::fromVariant( attrValue );
2876 QString jsonString;
2877 if ( !doc.isNull() )
2878 {
2879 jsonString = QString::fromUtf8( doc.toJson( QJsonDocument::Compact ).data() );
2880 }
2881 OGR_F_SetFieldString( poFeature.get(), ogrField, mCodec->fromUnicode( jsonString.constData() ) );
2882 break;
2883 }
2884
2885 // fall through to default for unsupported types
2886 if ( field.subType() == QMetaType::Type::QString )
2887 {
2888 QStringList list = attrValue.toStringList();
2889 if ( mSupportedListSubTypes.contains( QMetaType::Type::QString ) )
2890 {
2891 int count = list.count();
2892 char **lst = new char *[count + 1];
2893 if ( count > 0 )
2894 {
2895 int pos = 0;
2896 for ( const QString &string : list )
2897 {
2898 lst[pos] = CPLStrdup( mCodec->fromUnicode( string ).data() );
2899 pos++;
2900 }
2901 }
2902 lst[count] = nullptr;
2903 OGR_F_SetFieldStringList( poFeature.get(), ogrField, lst );
2904 CSLDestroy( lst );
2905 }
2906 else
2907 {
2908 OGR_F_SetFieldString( poFeature.get(), ogrField, mCodec->fromUnicode( list.join( ',' ) ).constData() );
2909 }
2910 break;
2911 }
2912 else if ( field.subType() == QMetaType::Type::Int )
2913 {
2914 const QVariantList list = attrValue.toList();
2915 if ( mSupportedListSubTypes.contains( QMetaType::Type::Int ) )
2916 {
2917 const int count = list.count();
2918 int *lst = new int[count];
2919 if ( count > 0 )
2920 {
2921 int pos = 0;
2922 for ( const QVariant &value : list )
2923 {
2924 lst[pos] = value.toInt();
2925 pos++;
2926 }
2927 }
2928 OGR_F_SetFieldIntegerList( poFeature.get(), ogrField, count, lst );
2929 delete [] lst;
2930 }
2931 else
2932 {
2933 QStringList strings;
2934 strings.reserve( list.size() );
2935 for ( const QVariant &value : list )
2936 {
2937 strings << QString::number( value.toInt() );
2938 }
2939 OGR_F_SetFieldString( poFeature.get(), ogrField, mCodec->fromUnicode( strings.join( ',' ) ).constData() );
2940 }
2941 break;
2942 }
2943 else if ( field.subType() == QMetaType::Type::Double )
2944 {
2945 const QVariantList list = attrValue.toList();
2946 if ( mSupportedListSubTypes.contains( QMetaType::Type::Double ) )
2947 {
2948 const int count = list.count();
2949 double *lst = new double[count];
2950 if ( count > 0 )
2951 {
2952 int pos = 0;
2953 for ( const QVariant &value : list )
2954 {
2955 lst[pos] = value.toDouble();
2956 pos++;
2957 }
2958 }
2959 OGR_F_SetFieldDoubleList( poFeature.get(), ogrField, count, lst );
2960 delete [] lst;
2961 }
2962 else
2963 {
2964 QStringList strings;
2965 strings.reserve( list.size() );
2966 for ( const QVariant &value : list )
2967 {
2968 strings << QString::number( value.toDouble() );
2969 }
2970 OGR_F_SetFieldString( poFeature.get(), ogrField, mCodec->fromUnicode( strings.join( ',' ) ).constData() );
2971 }
2972 break;
2973 }
2974 else if ( field.subType() == QMetaType::Type::LongLong )
2975 {
2976 const QVariantList list = attrValue.toList();
2977 if ( mSupportedListSubTypes.contains( QMetaType::Type::LongLong ) )
2978 {
2979 const int count = list.count();
2980 long long *lst = new long long[count];
2981 if ( count > 0 )
2982 {
2983 int pos = 0;
2984 for ( const QVariant &value : list )
2985 {
2986 lst[pos] = value.toLongLong();
2987 pos++;
2988 }
2989 }
2990 OGR_F_SetFieldInteger64List( poFeature.get(), ogrField, count, lst );
2991 delete [] lst;
2992 }
2993 else
2994 {
2995 QStringList strings;
2996 strings.reserve( list.size() );
2997 for ( const QVariant &value : list )
2998 {
2999 strings << QString::number( value.toLongLong() );
3000 }
3001 OGR_F_SetFieldString( poFeature.get(), ogrField, mCodec->fromUnicode( strings.join( ',' ) ).constData() );
3002 }
3003 break;
3004 }
3005 //intentional fall-through
3006 [[fallthrough]];
3007
3008 case QMetaType::Type::QVariantMap:
3009 {
3010 // handle GPKG conversion to JSON
3011 const char *pszDataSubTypes = GDALGetMetadataItem( OGRGetDriverByName( mOgrDriverName.toLocal8Bit().constData() ), GDAL_DMD_CREATIONFIELDDATASUBTYPES, nullptr );
3012 if ( pszDataSubTypes && strstr( pszDataSubTypes, "JSON" ) )
3013 {
3014 const QJsonDocument doc = QJsonDocument::fromVariant( attrValue );
3015 QString jsonString;
3016 if ( !doc.isNull() )
3017 {
3018 const QByteArray json { doc.toJson( QJsonDocument::Compact ) };
3019 jsonString = QString::fromUtf8( json.data() );
3020 }
3021 OGR_F_SetFieldString( poFeature.get(), ogrField, mCodec->fromUnicode( jsonString.constData() ) );
3022 break;
3023 }
3024 }
3025
3026 //intentional fall-through
3027 [[fallthrough]];
3028
3029
3030 default:
3031 mErrorMessage = QObject::tr( "Invalid variant type for field %1[%2]: received %3 with type %4" )
3032 .arg( mFields.at( fldIdx ).name() )
3033 .arg( ogrField )
3034 .arg( attrValue.typeName(),
3035 attrValue.toString() );
3036 QgsMessageLog::logMessage( mErrorMessage, QObject::tr( "OGR" ) );
3038 return nullptr;
3039 }
3040 }
3041
3043 {
3044 if ( feature.hasGeometry() )
3045 {
3046 // build geometry from WKB
3047 QgsGeometry geom = feature.geometry();
3048 if ( mCoordinateTransform )
3049 {
3050 // output dataset requires coordinate transform
3051 try
3052 {
3053 geom.transform( *mCoordinateTransform );
3054 }
3055 catch ( QgsCsException & )
3056 {
3057 QgsLogger::warning( QObject::tr( "Feature geometry failed to transform" ) );
3058 return nullptr;
3059 }
3060 }
3061
3062 // turn single geometry to multi geometry if needed
3065 {
3066 geom.convertToMultiType();
3067 }
3068
3069 if ( geom.wkbType() != mWkbType )
3070 {
3071 OGRGeometryH mGeom2 = nullptr;
3072
3073 // If requested WKB type is 25D and geometry WKB type is 3D,
3074 // we must force the use of 25D.
3076 {
3077 //ND: I suspect there's a bug here, in that this is NOT converting the geometry's WKB type,
3078 //so the exported WKB has a different type to what the OGRGeometry is expecting.
3079 //possibly this is handled already in OGR, but it should be fixed regardless by actually converting
3080 //geom to the correct WKB type
3081 Qgis::WkbType wkbType = geom.wkbType();
3082 if ( wkbType >= Qgis::WkbType::PointZ && wkbType <= Qgis::WkbType::MultiPolygonZ )
3083 {
3084 Qgis::WkbType wkbType25d = static_cast<Qgis::WkbType>( static_cast< quint32>( geom.wkbType() ) - static_cast< quint32>( Qgis::WkbType::PointZ ) + static_cast<quint32>( Qgis::WkbType::Point25D ) );
3085 mGeom2 = createEmptyGeometry( wkbType25d );
3086 }
3087 }
3088
3089 // drop m/z value if not present in output wkb type
3090 if ( !QgsWkbTypes::hasZ( mWkbType ) && QgsWkbTypes::hasZ( geom.wkbType() ) )
3091 geom.get()->dropZValue();
3092 if ( !QgsWkbTypes::hasM( mWkbType ) && QgsWkbTypes::hasM( geom.wkbType() ) )
3093 geom.get()->dropMValue();
3094
3095 // add m/z values if not present in the input wkb type -- this is needed for formats which determine
3096 // geometry type based on features, e.g. geojson
3097 if ( QgsWkbTypes::hasZ( mWkbType ) && !QgsWkbTypes::hasZ( geom.wkbType() ) )
3098 {
3099 if ( mOgrDriverName == QLatin1String( "ESRI Shapefile" ) )
3100 geom.get()->addZValue( std::numeric_limits<double>::quiet_NaN() );
3101 else
3102 geom.get()->addZValue( 0 );
3103 }
3104 if ( QgsWkbTypes::hasM( mWkbType ) && !QgsWkbTypes::hasM( geom.wkbType() ) )
3105 {
3106 if ( mOgrDriverName == QLatin1String( "ESRI Shapefile" ) )
3107 geom.get()->addMValue( std::numeric_limits<double>::quiet_NaN() );
3108 else
3109 geom.get()->addMValue( 0 );
3110 }
3111
3112 if ( !mGeom2 )
3113 {
3114 // there's a problem when layer type is set as wkbtype Polygon
3115 // although there are also features of type MultiPolygon
3116 // (at least in OGR provider)
3117 // If the feature's wkbtype is different from the layer's wkbtype,
3118 // try to export it too.
3119 //
3120 // Btw. OGRGeometry must be exactly of the type of the geometry which it will receive
3121 // i.e. Polygons can't be imported to OGRMultiPolygon
3122 mGeom2 = createEmptyGeometry( geom.wkbType() );
3123 }
3124
3125 if ( !mGeom2 )
3126 {
3127 mErrorMessage = QObject::tr( "Feature geometry not imported (OGR error: %1)" )
3128 .arg( QString::fromUtf8( CPLGetLastErrorMsg() ) );
3130 QgsMessageLog::logMessage( mErrorMessage, QObject::tr( "OGR" ) );
3131 return nullptr;
3132 }
3133
3135 if ( mOgrDriverName == QLatin1String( "ESRI Shapefile" ) )
3137
3138 QByteArray wkb( geom.asWkb( wkbFlags ) );
3139 OGRErr err = OGR_G_ImportFromWkb( mGeom2, reinterpret_cast<unsigned char *>( const_cast<char *>( wkb.constData() ) ), wkb.length() );
3140 if ( err != OGRERR_NONE )
3141 {
3142 mErrorMessage = QObject::tr( "Feature geometry not imported (OGR error: %1)" )
3143 .arg( QString::fromUtf8( CPLGetLastErrorMsg() ) );
3145 QgsMessageLog::logMessage( mErrorMessage, QObject::tr( "OGR" ) );
3146 return nullptr;
3147 }
3148
3149 // pass ownership to geometry
3150 OGR_F_SetGeometryDirectly( poFeature.get(), mGeom2 );
3151 }
3152 else // wkb type matches
3153 {
3155 if ( mOgrDriverName == QLatin1String( "ESRI Shapefile" ) )
3157
3158 QByteArray wkb( geom.asWkb( wkbFlags ) );
3159 OGRGeometryH ogrGeom = createEmptyGeometry( mWkbType );
3160 OGRErr err = OGR_G_ImportFromWkb( ogrGeom, reinterpret_cast<unsigned char *>( const_cast<char *>( wkb.constData() ) ), wkb.length() );
3161 if ( err != OGRERR_NONE )
3162 {
3163 mErrorMessage = QObject::tr( "Feature geometry not imported (OGR error: %1)" )
3164 .arg( QString::fromUtf8( CPLGetLastErrorMsg() ) );
3166 QgsMessageLog::logMessage( mErrorMessage, QObject::tr( "OGR" ) );
3167 return nullptr;
3168 }
3169
3170 // set geometry (ownership is passed to OGR)
3171 OGR_F_SetGeometryDirectly( poFeature.get(), ogrGeom );
3172 }
3173 }
3174 else
3175 {
3176 OGR_F_SetGeometryDirectly( poFeature.get(), createEmptyGeometry( mWkbType ) );
3177 }
3178 }
3179 return poFeature;
3180}
3181
3182void QgsVectorFileWriter::resetMap( const QgsAttributeList &attributes )
3183{
3184 QMap<int, int> omap( mAttrIdxToOgrIdx );
3185 mAttrIdxToOgrIdx.clear();
3186 for ( int i = 0; i < attributes.size(); i++ )
3187 {
3188 if ( omap.find( i ) != omap.end() )
3189 mAttrIdxToOgrIdx.insert( attributes[i], omap[i] );
3190 }
3191}
3192
3193bool QgsVectorFileWriter::writeFeature( OGRLayerH layer, OGRFeatureH feature )
3194{
3195 if ( OGR_L_CreateFeature( layer, feature ) != OGRERR_NONE )
3196 {
3197 mErrorMessage = QObject::tr( "Feature creation error (OGR error: %1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) );
3199 QgsMessageLog::logMessage( mErrorMessage, QObject::tr( "OGR" ) );
3200 return false;
3201 }
3202 return true;
3203}
3204
3206{
3207 if ( mUsingTransaction )
3208 {
3209 if ( OGRERR_NONE != OGR_L_CommitTransaction( mLayer ) )
3210 {
3211 QgsDebugError( QStringLiteral( "Error while committing transaction on OGRLayer." ) );
3212 }
3213 }
3214 mDS.reset();
3215
3216 if ( mOgrRef )
3217 {
3218 OSRRelease( mOgrRef );
3219 }
3220}
3221
3224 const QString &fileName,
3225 const QString &fileEncoding,
3226 const QgsCoordinateReferenceSystem &destCRS,
3227 const QString &driverName,
3228 bool onlySelected,
3229 QString *errorMessage,
3230 const QStringList &datasourceOptions,
3231 const QStringList &layerOptions,
3232 bool skipAttributeCreation,
3233 QString *newFilename,
3234 Qgis::FeatureSymbologyExport symbologyExport,
3235 double symbologyScale,
3236 const QgsRectangle *filterExtent,
3237 Qgis::WkbType overrideGeometryType,
3238 bool forceMulti,
3239 bool includeZ,
3240 const QgsAttributeList &attributes,
3241 FieldValueConverter *fieldValueConverter,
3242 QString *newLayer )
3243{
3245 if ( destCRS.isValid() && layer )
3246 {
3247 ct = QgsCoordinateTransform( layer->crs(), destCRS, layer->transformContext() );
3248 }
3249
3250 SaveVectorOptions options;
3251 options.fileEncoding = fileEncoding;
3252 options.ct = ct;
3253 options.driverName = driverName;
3254 options.onlySelectedFeatures = onlySelected;
3255 options.datasourceOptions = datasourceOptions;
3256 options.layerOptions = layerOptions;
3257 options.skipAttributeCreation = skipAttributeCreation;
3260 if ( filterExtent )
3261 options.filterExtent = *filterExtent;
3262 options.overrideGeometryType = overrideGeometryType;
3263 options.forceMulti = forceMulti;
3264 options.includeZ = includeZ;
3265 options.attributes = attributes;
3266 options.fieldValueConverter = fieldValueConverter;
3267 return writeAsVectorFormatV3( layer, fileName, layer->transformContext(), options, errorMessage, newFilename, newLayer );
3268}
3269
3271 const QString &fileName,
3272 const QString &fileEncoding,
3273 const QgsCoordinateTransform &ct,
3274 const QString &driverName,
3275 bool onlySelected,
3276 QString *errorMessage,
3277 const QStringList &datasourceOptions,
3278 const QStringList &layerOptions,
3279 bool skipAttributeCreation,
3280 QString *newFilename,
3281 Qgis::FeatureSymbologyExport symbologyExport,
3282 double symbologyScale,
3283 const QgsRectangle *filterExtent,
3284 Qgis::WkbType overrideGeometryType,
3285 bool forceMulti,
3286 bool includeZ,
3287 const QgsAttributeList &attributes,
3288 FieldValueConverter *fieldValueConverter,
3289 QString *newLayer )
3290{
3291 SaveVectorOptions options;
3292 options.fileEncoding = fileEncoding;
3293 options.ct = ct;
3294 options.driverName = driverName;
3295 options.onlySelectedFeatures = onlySelected;
3296 options.datasourceOptions = datasourceOptions;
3297 options.layerOptions = layerOptions;
3298 options.skipAttributeCreation = skipAttributeCreation;
3301 if ( filterExtent )
3302 options.filterExtent = *filterExtent;
3303 options.overrideGeometryType = overrideGeometryType;
3304 options.forceMulti = forceMulti;
3305 options.includeZ = includeZ;
3306 options.attributes = attributes;
3307 options.fieldValueConverter = fieldValueConverter;
3308 return writeAsVectorFormatV3( layer, fileName, layer->transformContext(), options, errorMessage, newFilename, newLayer );
3309}
3310
3311
3313 : driverName( QStringLiteral( "GPKG" ) )
3314{
3315}
3316
3317
3318
3319QgsVectorFileWriter::WriterError QgsVectorFileWriter::prepareWriteAsVectorFormat( QgsVectorLayer *layer, const QgsVectorFileWriter::SaveVectorOptions &options, QgsVectorFileWriter::PreparedWriterDetails &details )
3320{
3321 if ( !layer || !layer->isValid() )
3322 {
3323 return ErrInvalidLayer;
3324 }
3325
3326 if ( layer->renderer() )
3327 details.renderer.reset( layer->renderer()->clone() );
3328 details.sourceCrs = layer->crs();
3329 details.sourceWkbType = layer->wkbType();
3330 details.sourceFields = layer->fields();
3331 details.providerType = layer->providerType();
3332 details.featureCount = options.onlySelectedFeatures ? layer->selectedFeatureCount() : layer->featureCount();
3333 if ( layer->dataProvider() )
3334 details.dataSourceUri = layer->dataProvider()->dataSourceUri();
3335 details.storageType = layer->storageType();
3336 details.selectedFeatureIds = layer->selectedFeatureIds();
3337 details.providerUriParams = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
3338
3339 if ( details.storageType == QLatin1String( "ESRI Shapefile" ) )
3340 {
3342 if ( options.onlySelectedFeatures )
3343 {
3344 req.setFilterFids( details.selectedFeatureIds );
3345 }
3346 req.setNoAttributes();
3347 details.geometryTypeScanIterator = layer->getFeatures( req );
3348 }
3349
3350 details.expressionContext = QgsExpressionContext( QgsExpressionContextUtils::globalProjectLayerScopes( layer ) );
3351 details.renderContext.setExpressionContext( details.expressionContext );
3352 details.renderContext.setRendererScale( options.symbologyScale );
3353
3354 details.shallTransform = false;
3355 if ( options.ct.isValid() )
3356 {
3357 // This means we should transform
3358 details.outputCrs = options.ct.destinationCrs();
3359 details.shallTransform = true;
3360 }
3361 else
3362 {
3363 // This means we shouldn't transform, use source CRS as output (if defined)
3364 details.outputCrs = details.sourceCrs;
3365 }
3366
3367 details.destWkbType = details.sourceWkbType;
3369 {
3370 details.destWkbType = QgsWkbTypes::flatType( options.overrideGeometryType );
3371 if ( QgsWkbTypes::hasZ( options.overrideGeometryType ) || options.includeZ )
3372 details.destWkbType = QgsWkbTypes::addZ( details.destWkbType );
3373 }
3374 if ( options.forceMulti )
3375 {
3376 details.destWkbType = QgsWkbTypes::multiType( details.destWkbType );
3377 }
3378
3379 details.attributes = options.attributes;
3380 if ( options.skipAttributeCreation )
3381 details.attributes.clear();
3382 else if ( details.attributes.isEmpty() )
3383 {
3384 const QgsAttributeList allAttributes = details.sourceFields.allAttributesList();
3385 for ( int idx : allAttributes )
3386 {
3387 QgsField fld = details.sourceFields.at( idx );
3388 if ( details.providerType == QLatin1String( "oracle" ) && fld.typeName().contains( QLatin1String( "SDO_GEOMETRY" ) ) )
3389 continue;
3390 details.attributes.append( idx );
3391 }
3392 }
3393
3394 if ( !details.attributes.isEmpty() )
3395 {
3396 for ( int attrIdx : std::as_const( details.attributes ) )
3397 {
3398 if ( details.sourceFields.exists( attrIdx ) )
3399 {
3400 QgsField field = details.sourceFields.at( attrIdx );
3401 field.setName( options.attributesExportNames.value( attrIdx, field.name() ) );
3402 details.outputFields.append( field );
3403 }
3404 else
3405 {
3406 QgsDebugError( QStringLiteral( "No such source field with index '%1' available." ).arg( attrIdx ) );
3407 }
3408 }
3409 }
3410
3411 // not ideal - would be nice to avoid this happening in the preparation step if possible,
3412 // but currently requires access to the layer's minimumValue/maximumValue methods
3413 if ( details.providerType == QLatin1String( "spatialite" ) )
3414 {
3415 for ( int i = 0; i < details.outputFields.size(); i++ )
3416 {
3417 if ( details.outputFields.at( i ).type() == QMetaType::Type::LongLong )
3418 {
3419 QVariant min;
3420 QVariant max;
3421 layer->minimumAndMaximumValue( i, min, max );
3422 if ( std::max( std::llabs( min.toLongLong() ), std::llabs( max.toLongLong() ) ) < std::numeric_limits<int>::max() )
3423 {
3424 details.outputFields[i].setType( QMetaType::Type::Int );
3425 }
3426 }
3427 }
3428 }
3429
3430
3431 //add possible attributes needed by renderer
3432 addRendererAttributes( details.renderer.get(), details.renderContext, details.sourceFields, details.attributes );
3433
3435 req.setSubsetOfAttributes( details.attributes );
3436 if ( options.onlySelectedFeatures )
3437 req.setFilterFids( details.selectedFeatureIds );
3438
3439 if ( !options.filterExtent.isNull() )
3440 {
3441 QgsRectangle filterRect = options.filterExtent;
3442 bool useFilterRect = true;
3443 if ( details.shallTransform )
3444 {
3445 try
3446 {
3447 // map filter rect back from destination CRS to layer CRS
3448 QgsCoordinateTransform extentTransform = options.ct;
3449 extentTransform.setBallparkTransformsAreAppropriate( true );
3450 filterRect = extentTransform.transformBoundingBox( filterRect, Qgis::TransformDirection::Reverse );
3451 }
3452 catch ( QgsCsException & )
3453 {
3454 useFilterRect = false;
3455 }
3456 }
3457 if ( useFilterRect )
3458 {
3459 req.setFilterRect( filterRect );
3460 }
3461 details.filterRectGeometry = QgsGeometry::fromRect( options.filterExtent );
3462 details.filterRectEngine.reset( QgsGeometry::createGeometryEngine( details.filterRectGeometry.constGet() ) );
3463 details.filterRectEngine->prepareGeometry();
3464 }
3465 details.sourceFeatureIterator = layer->getFeatures( req );
3466
3467 if ( !options.sourceDatabaseProviderConnection )
3468 {
3469 details.sourceDatabaseProviderConnection.reset( QgsMapLayerUtils::databaseConnection( layer ) );
3470 }
3471
3472 return NoError;
3473}
3474
3475QgsVectorFileWriter::WriterError QgsVectorFileWriter::writeAsVectorFormat( PreparedWriterDetails &details, const QString &fileName, const QgsVectorFileWriter::SaveVectorOptions &options, QString *newFilename, QString *errorMessage, QString *newLayer )
3476{
3477 return writeAsVectorFormatV2( details, fileName, QgsCoordinateTransformContext(), options, newFilename, newLayer, errorMessage );
3478}
3479
3480QgsVectorFileWriter::WriterError QgsVectorFileWriter::writeAsVectorFormatV2( PreparedWriterDetails &details, const QString &fileName, const QgsCoordinateTransformContext &transformContext, const QgsVectorFileWriter::SaveVectorOptions &options, QString *newFilename, QString *newLayer, QString *errorMessage )
3481{
3482 Qgis::WkbType destWkbType = details.destWkbType;
3483
3484 int lastProgressReport = 0;
3485 long long total = details.featureCount;
3486
3487 // Special rules for OGR layers
3488 if ( details.providerType == QLatin1String( "ogr" ) && !details.dataSourceUri.isEmpty() )
3489 {
3490 QString srcFileName( details.providerUriParams.value( QStringLiteral( "path" ) ).toString() );
3491 if ( QFile::exists( srcFileName ) && QFileInfo( fileName ).canonicalFilePath() == QFileInfo( srcFileName ).canonicalFilePath() )
3492 {
3493 // Check the layer name too if it's a GPKG/SpatiaLite/SQLite OGR driver (pay attention: camel case in layerName)
3494 QgsDataSourceUri uri( details.dataSourceUri );
3495 if ( !( ( options.driverName == QLatin1String( "GPKG" ) ||
3496 options.driverName == QLatin1String( "SpatiaLite" ) ||
3497 options.driverName == QLatin1String( "SQLite" ) ) &&
3498 options.layerName != details.providerUriParams.value( QStringLiteral( "layerName" ) ) ) )
3499 {
3500 if ( errorMessage )
3501 *errorMessage = QObject::tr( "Cannot overwrite a OGR layer in place" );
3502 return ErrCreateDataSource;
3503 }
3504 }
3505
3506 // Shapefiles might contain multi types although wkbType() only reports singles
3507 if ( details.storageType == QLatin1String( "ESRI Shapefile" ) && !QgsWkbTypes::isMultiType( destWkbType ) )
3508 {
3509 QgsFeatureIterator fit = details.geometryTypeScanIterator;
3510 QgsFeature fet;
3511 long scanned = 0;
3512 while ( fit.nextFeature( fet ) )
3513 {
3514 if ( options.feedback && options.feedback->isCanceled() )
3515 {
3516 return Canceled;
3517 }
3518 if ( options.feedback )
3519 {
3520 //dedicate first 5% of progress bar to this scan
3521 int newProgress = static_cast<int>( ( 5.0 * scanned ) / total );
3522 if ( newProgress != lastProgressReport )
3523 {
3524 lastProgressReport = newProgress;
3525 options.feedback->setProgress( lastProgressReport );
3526 }
3527 }
3528
3529 if ( fet.hasGeometry() && QgsWkbTypes::isMultiType( fet.geometry().wkbType() ) )
3530 {
3531 destWkbType = QgsWkbTypes::multiType( destWkbType );
3532 break;
3533 }
3534 scanned++;
3535 }
3536 }
3537 }
3538
3539 QString tempNewFilename;
3540 QString tempNewLayer;
3541
3542 QgsVectorFileWriter::SaveVectorOptions newOptions = options;
3543 if ( !newOptions.sourceDatabaseProviderConnection )
3544 {
3545 newOptions.sourceDatabaseProviderConnection = details.sourceDatabaseProviderConnection.get();
3546 }
3547
3548 std::unique_ptr< QgsVectorFileWriter > writer( create( fileName, details.outputFields, destWkbType, details.outputCrs, transformContext, newOptions, QgsFeatureSink::SinkFlags(), &tempNewFilename, &tempNewLayer ) );
3549 writer->setSymbologyScale( options.symbologyScale );
3550
3551 if ( newFilename )
3552 *newFilename = tempNewFilename;
3553
3554 if ( newLayer )
3555 *newLayer = tempNewLayer;
3556
3557 if ( newFilename )
3558 {
3559 QgsDebugMsgLevel( "newFilename = " + *newFilename, 2 );
3560 }
3561
3562 // check whether file creation was successful
3563 WriterError err = writer->hasError();
3564 if ( err != NoError )
3565 {
3566 if ( errorMessage )
3567 *errorMessage = writer->errorMessage();
3568 return err;
3569 }
3570
3571 if ( errorMessage )
3572 {
3573 errorMessage->clear();
3574 }
3575
3576 QgsFeature fet;
3577
3578 //create symbol table if needed
3579 if ( writer->symbologyExport() != Qgis::FeatureSymbologyExport::NoSymbology )
3580 {
3581 //writer->createSymbolLayerTable( layer, writer->mDS );
3582 }
3583
3584 switch ( writer->symbologyExport() )
3585 {
3587 {
3588 QgsFeatureRenderer *r = details.renderer.get();
3590 && r->usingSymbolLevels() )
3591 {
3592 QgsVectorFileWriter::WriterError error = writer->exportFeaturesSymbolLevels( details, details.sourceFeatureIterator, options.ct, errorMessage );
3593 return ( error == NoError ) ? NoError : ErrFeatureWriteFailed;
3594 }
3595 break;
3596 }
3599 break;
3600 }
3601
3602 int n = 0, errors = 0;
3603
3604 //unit type
3605 Qgis::DistanceUnit mapUnits = details.sourceCrs.mapUnits();
3606 if ( options.ct.isValid() )
3607 {
3608 mapUnits = options.ct.destinationCrs().mapUnits();
3609 }
3610
3611 writer->startRender( details.renderer.get(), details.sourceFields );
3612
3613 writer->resetMap( details.attributes );
3614 // Reset mFields to layer fields, and not just exported fields
3615 writer->mFields = details.sourceFields;
3616
3617 // write all features
3618 long saved = 0;
3619 int initialProgress = lastProgressReport;
3620 while ( details.sourceFeatureIterator.nextFeature( fet ) )
3621 {
3622 if ( options.feedback && options.feedback->isCanceled() )
3623 {
3624 return Canceled;
3625 }
3626
3627 saved++;
3628 if ( options.feedback )
3629 {
3630 //avoid spamming progress reports
3631 int newProgress = static_cast<int>( initialProgress + ( ( 100.0 - initialProgress ) * saved ) / total );
3632 if ( newProgress < 100 && newProgress != lastProgressReport )
3633 {
3634 lastProgressReport = newProgress;
3635 options.feedback->setProgress( lastProgressReport );
3636 }
3637 }
3638
3639 if ( details.shallTransform )
3640 {
3641 try
3642 {
3643 if ( fet.hasGeometry() )
3644 {
3645 QgsGeometry g = fet.geometry();
3646 g.transform( options.ct );
3647 fet.setGeometry( g );
3648 }
3649 }
3650 catch ( QgsCsException &e )
3651 {
3652 const QString msg = QObject::tr( "Failed to transform feature with ID '%1'. Writing stopped. (Exception: %2)" )
3653 .arg( fet.id() ).arg( e.what() );
3654 QgsLogger::warning( msg );
3655 if ( errorMessage )
3656 *errorMessage = msg;
3657
3658 return ErrProjection;
3659 }
3660 }
3661
3662 if ( fet.hasGeometry() && details.filterRectEngine && !details.filterRectEngine->intersects( fet.geometry().constGet() ) )
3663 continue;
3664
3665 if ( details.attributes.empty() && options.skipAttributeCreation )
3666 {
3667 fet.initAttributes( 0 );
3668 }
3669
3670 if ( !writer->addFeatureWithStyle( fet, writer->mRenderer.get(), mapUnits ) )
3671 {
3672 WriterError err = writer->hasError();
3673 if ( err != NoError && errorMessage )
3674 {
3675 if ( errorMessage->isEmpty() )
3676 {
3677 *errorMessage = QObject::tr( "Feature write errors:" );
3678 }
3679 *errorMessage += '\n' + writer->errorMessage();
3680 }
3681 errors++;
3682
3683 if ( errors > 1000 )
3684 {
3685 if ( errorMessage )
3686 {
3687 *errorMessage += QObject::tr( "Stopping after %n error(s)", nullptr, errors );
3688 }
3689
3690 n = -1;
3691 break;
3692 }
3693 }
3694 n++;
3695 }
3696
3697 writer->stopRender();
3698
3699 if ( errors > 0 && errorMessage && n > 0 )
3700 {
3701 *errorMessage += QObject::tr( "\nOnly %1 of %2 features written." ).arg( n - errors ).arg( n );
3702 }
3703
3704 writer.reset();
3705
3706 bool metadataFailure = false;
3707 if ( options.saveMetadata )
3708 {
3709 QString uri = QgsProviderRegistry::instance()->encodeUri( QStringLiteral( "ogr" ), QVariantMap
3710 {
3711 {QStringLiteral( "path" ), tempNewFilename },
3712 {QStringLiteral( "layerName" ), tempNewLayer }
3713 } );
3714
3715 try
3716 {
3717 QString error;
3718 if ( !QgsProviderRegistry::instance()->saveLayerMetadata( QStringLiteral( "ogr" ), uri, options.layerMetadata, error ) )
3719 {
3720 if ( errorMessage )
3721 {
3722 if ( !errorMessage->isEmpty() )
3723 *errorMessage += '\n';
3724 *errorMessage += error;
3725 }
3726 metadataFailure = true;
3727 }
3728 }
3729 catch ( QgsNotSupportedException &e )
3730 {
3731 if ( errorMessage )
3732 {
3733 if ( !errorMessage->isEmpty() )
3734 *errorMessage += '\n';
3735 *errorMessage += e.what();
3736 }
3737 metadataFailure = true;
3738 }
3739 }
3740
3741 return errors == 0 ? ( !metadataFailure ? NoError : ErrSavingMetadata ) : ErrFeatureWriteFailed;
3742}
3743
3745 const QString &fileName,
3746 const SaveVectorOptions &options,
3747 QString *newFilename,
3748 QString *errorMessage,
3749 QString *newLayer )
3750{
3751 QgsVectorFileWriter::PreparedWriterDetails details;
3752 WriterError err = prepareWriteAsVectorFormat( layer, options, details );
3753 if ( err != NoError )
3754 return err;
3755
3756 return writeAsVectorFormatV2( details, fileName, layer->transformContext(), options, newFilename, newLayer, errorMessage );
3757}
3758
3760 const QString &fileName,
3761 const QgsCoordinateTransformContext &transformContext,
3762 const SaveVectorOptions &options,
3763 QString *newFilename,
3764 QString *newLayer,
3765 QString *errorMessage )
3766{
3767 QgsVectorFileWriter::PreparedWriterDetails details;
3768 WriterError err = prepareWriteAsVectorFormat( layer, options, details );
3769 if ( err != NoError )
3770 return err;
3771
3772 return writeAsVectorFormatV2( details, fileName, transformContext, options, errorMessage, newFilename, newLayer );
3773}
3774
3775QgsVectorFileWriter::WriterError QgsVectorFileWriter::writeAsVectorFormatV3( QgsVectorLayer *layer, const QString &fileName, const QgsCoordinateTransformContext &transformContext, const QgsVectorFileWriter::SaveVectorOptions &options, QString *errorMessage, QString *newFilename, QString *newLayer )
3776{
3777 QgsVectorFileWriter::PreparedWriterDetails details;
3778 WriterError err = prepareWriteAsVectorFormat( layer, options, details );
3779 if ( err != NoError )
3780 return err;
3781
3782 return writeAsVectorFormatV2( details, fileName, transformContext, options, newFilename, newLayer, errorMessage );
3783}
3784
3785bool QgsVectorFileWriter::deleteShapeFile( const QString &fileName )
3786{
3787 QFileInfo fi( fileName );
3788 QDir dir = fi.dir();
3789
3790 QStringList filter;
3791 for ( const char *suffix : { ".shp", ".shx", ".dbf", ".prj", ".qix", ".qpj", ".cpg", ".sbn", ".sbx", ".idm", ".ind" } )
3792 {
3793 filter << fi.completeBaseName() + suffix;
3794 }
3795
3796 bool ok = true;
3797 const auto constEntryList = dir.entryList( filter );
3798 for ( const QString &file : constEntryList )
3799 {
3800 QFile f( dir.canonicalPath() + '/' + file );
3801 if ( !f.remove() )
3802 {
3803 QgsDebugError( QStringLiteral( "Removing file %1 failed: %2" ).arg( file, f.errorString() ) );
3804 ok = false;
3805 }
3806 }
3807
3808 return ok;
3809}
3810
3812{
3813 mSymbologyScale = d;
3814 mRenderContext.setRendererScale( mSymbologyScale );
3815}
3816
3818{
3819 QStringList driverNames;
3820 const QSet< QString > multiLayerExtensions = qgis::listToSet( QgsGdalUtils::multiLayerFileExtensions() );
3821
3822 for ( int i = 0; i < GDALGetDriverCount(); ++i )
3823 {
3824 GDALDriverH driver = GDALGetDriver( i );
3825 if ( !driver )
3826 {
3827 QgsLogger::warning( "unable to get driver " + QString::number( i ) );
3828 continue;
3829 }
3830
3831 const QString driverExtensions = GDALGetMetadataItem( driver, GDAL_DMD_EXTENSIONS, "" );
3832 if ( driverExtensions.isEmpty() )
3833 continue;
3834
3835 const QSet< QString > splitExtensions = qgis::listToSet( driverExtensions.split( ' ', Qt::SkipEmptyParts ) );
3836 if ( splitExtensions.intersects( multiLayerExtensions ) )
3837 {
3838 driverNames << OGR_Dr_GetName( driver );
3839 }
3840 }
3841 return driverNames;
3842}
3843
3844QList< QgsVectorFileWriter::FilterFormatDetails > QgsVectorFileWriter::supportedFiltersAndFormats( const VectorFormatOptions options )
3845{
3846 static QReadWriteLock sFilterLock;
3847 static QMap< VectorFormatOptions, QList< QgsVectorFileWriter::FilterFormatDetails > > sFilters;
3848
3849 QgsReadWriteLocker locker( sFilterLock, QgsReadWriteLocker::Read );
3850
3851 const auto it = sFilters.constFind( options );
3852 if ( it != sFilters.constEnd() )
3853 return it.value();
3854
3856 QList< QgsVectorFileWriter::FilterFormatDetails > results;
3857
3859 int const drvCount = OGRGetDriverCount();
3860
3861 const QStringList multiLayerDrivers = multiLayerFormats();
3862
3863 for ( int i = 0; i < drvCount; ++i )
3864 {
3865 OGRSFDriverH drv = OGRGetDriver( i );
3866 if ( drv )
3867 {
3868 const QString drvName = OGR_Dr_GetName( drv );
3869
3870 if ( options & SupportsMultipleLayers )
3871 {
3872 if ( !multiLayerDrivers.contains( drvName ) )
3873 continue;
3874 }
3875
3876 GDALDriverH gdalDriver = GDALGetDriverByName( drvName.toLocal8Bit().constData() );
3877 bool nonSpatialFormat = false;
3878 if ( gdalDriver )
3879 {
3880 nonSpatialFormat = GDALGetMetadataItem( gdalDriver, GDAL_DCAP_NONSPATIAL, nullptr );
3881 }
3882
3883 if ( OGR_Dr_TestCapability( drv, "CreateDataSource" ) != 0 )
3884 {
3885 if ( options & SkipNonSpatialFormats )
3886 {
3887 // skip non-spatial formats
3888 if ( nonSpatialFormat )
3889 continue;
3890 }
3891
3892 QString filterString = filterForDriver( drvName );
3893 if ( filterString.isEmpty() )
3894 continue;
3895
3896 MetaData metadata;
3897 QStringList globs;
3898 if ( driverMetadata( drvName, metadata ) && !metadata.glob.isEmpty() )
3899 {
3900 globs = metadata.glob.toLower().split( ' ' );
3901 }
3902
3903 FilterFormatDetails details;
3904 details.driverName = drvName;
3905 details.filterString = filterString;
3906 details.globs = globs;
3907
3908 results << details;
3909 }
3910 }
3911 }
3912
3913 std::sort( results.begin(), results.end(), [options]( const FilterFormatDetails & a, const FilterFormatDetails & b ) -> bool
3914 {
3915 if ( options & SortRecommended )
3916 {
3917 if ( a.driverName == QLatin1String( "GPKG" ) )
3918 return true; // Make https://twitter.com/shapefiIe a sad little fellow
3919 else if ( b.driverName == QLatin1String( "GPKG" ) )
3920 return false;
3921 else if ( a.driverName == QLatin1String( "ESRI Shapefile" ) )
3922 return true;
3923 else if ( b.driverName == QLatin1String( "ESRI Shapefile" ) )
3924 return false;
3925 }
3926
3927 return a.filterString.toLower().localeAwareCompare( b.filterString.toLower() ) < 0;
3928 } );
3929
3930 sFilters.insert( options, results );
3931 return results;
3932}
3933
3935{
3936 const auto formats = supportedFiltersAndFormats( options );
3937 QSet< QString > extensions;
3938
3939 const thread_local QRegularExpression rx( QStringLiteral( "\\*\\.(.*)$" ) );
3940
3941 for ( const FilterFormatDetails &format : formats )
3942 {
3943 for ( const QString &glob : format.globs )
3944 {
3945 const QRegularExpressionMatch match = rx.match( glob );
3946 if ( !match.hasMatch() )
3947 continue;
3948
3949 const QString matched = match.captured( 1 );
3950 extensions.insert( matched );
3951 }
3952 }
3953
3954 QStringList extensionList( extensions.constBegin(), extensions.constEnd() );
3955
3956 std::sort( extensionList.begin(), extensionList.end(), [options]( const QString & a, const QString & b ) -> bool
3957 {
3958 if ( options & SortRecommended )
3959 {
3960 if ( a == QLatin1String( "gpkg" ) )
3961 return true; // Make https://twitter.com/shapefiIe a sad little fellow
3962 else if ( b == QLatin1String( "gpkg" ) )
3963 return false;
3964 else if ( a == QLatin1String( "shp" ) )
3965 return true;
3966 else if ( b == QLatin1String( "shp" ) )
3967 return false;
3968 }
3969
3970 return a.toLower().localeAwareCompare( b.toLower() ) < 0;
3971 } );
3972
3973 return extensionList;
3974}
3975
3976QList< QgsVectorFileWriter::DriverDetails > QgsVectorFileWriter::ogrDriverList( const VectorFormatOptions options )
3977{
3978 QList< QgsVectorFileWriter::DriverDetails > results;
3979
3981 const int drvCount = OGRGetDriverCount();
3982
3983 const QStringList multiLayerDrivers = multiLayerFormats();
3984
3985 QStringList writableDrivers;
3986 for ( int i = 0; i < drvCount; ++i )
3987 {
3988 OGRSFDriverH drv = OGRGetDriver( i );
3989 if ( drv )
3990 {
3991 const QString drvName = OGR_Dr_GetName( drv );
3992
3993 if ( options & SupportsMultipleLayers )
3994 {
3995 if ( !multiLayerDrivers.contains( drvName ) )
3996 continue;
3997 }
3998
3999 if ( options & SkipNonSpatialFormats )
4000 {
4001 // skip non-spatial formats
4002 // TODO - use GDAL metadata to determine this, when support exists in GDAL
4003 if ( drvName == QLatin1String( "ODS" ) || drvName == QLatin1String( "XLSX" ) || drvName == QLatin1String( "XLS" ) )
4004 continue;
4005 }
4006
4007 if ( drvName == QLatin1String( "ESRI Shapefile" ) )
4008 {
4009 writableDrivers << QStringLiteral( "DBF file" );
4010 }
4011 if ( OGR_Dr_TestCapability( drv, "CreateDataSource" ) != 0 )
4012 {
4013 // Add separate format for Mapinfo MIF (MITAB is OGR default)
4014 if ( drvName == QLatin1String( "MapInfo File" ) )
4015 {
4016 writableDrivers << QStringLiteral( "MapInfo MIF" );
4017 }
4018 else if ( drvName == QLatin1String( "SQLite" ) )
4019 {
4020 // Unfortunately it seems that there is no simple way to detect if
4021 // OGR SQLite driver is compiled with SpatiaLite support.
4022 // We have HAVE_SPATIALITE in QGIS, but that may differ from OGR
4023 // http://lists.osgeo.org/pipermail/gdal-dev/2012-November/034580.html
4024 // -> test if creation fails
4025 QString option = QStringLiteral( "SPATIALITE=YES" );
4026 char *options[2] = { CPLStrdup( option.toLocal8Bit().constData() ), nullptr };
4027 OGRSFDriverH poDriver;
4029 poDriver = OGRGetDriverByName( drvName.toLocal8Bit().constData() );
4030 if ( poDriver )
4031 {
4032 gdal::ogr_datasource_unique_ptr ds( OGR_Dr_CreateDataSource( poDriver, QStringLiteral( "/vsimem/spatialitetest.sqlite" ).toUtf8().constData(), options ) );
4033 if ( ds )
4034 {
4035 writableDrivers << QStringLiteral( "SpatiaLite" );
4036 OGR_Dr_DeleteDataSource( poDriver, QStringLiteral( "/vsimem/spatialitetest.sqlite" ).toUtf8().constData() );
4037 }
4038 }
4039 CPLFree( options[0] );
4040 }
4041 writableDrivers << drvName;
4042 }
4043 }
4044 }
4045
4046 results.reserve( writableDrivers.count() );
4047 for ( const QString &drvName : std::as_const( writableDrivers ) )
4048 {
4049 MetaData metadata;
4050 if ( driverMetadata( drvName, metadata ) && !metadata.trLongName.isEmpty() )
4051 {
4052 DriverDetails details;
4053 details.driverName = drvName;
4054 details.longName = metadata.trLongName;
4055 results << details;
4056 }
4057 }
4058
4059 std::sort( results.begin(), results.end(), [options]( const DriverDetails & a, const DriverDetails & b ) -> bool
4060 {
4061 if ( options & SortRecommended )
4062 {
4063 if ( a.driverName == QLatin1String( "GPKG" ) )
4064 return true; // Make https://twitter.com/shapefiIe a sad little fellow
4065 else if ( b.driverName == QLatin1String( "GPKG" ) )
4066 return false;
4067 else if ( a.driverName == QLatin1String( "ESRI Shapefile" ) )
4068 return true;
4069 else if ( b.driverName == QLatin1String( "ESRI Shapefile" ) )
4070 return false;
4071 }
4072
4073 return a.longName.toLower().localeAwareCompare( b.longName.toLower() ) < 0;
4074 } );
4075 return results;
4076}
4077
4078QString QgsVectorFileWriter::driverForExtension( const QString &extension )
4079{
4080 QString ext = extension.trimmed();
4081 if ( ext.isEmpty() )
4082 return QString();
4083
4084 if ( ext.startsWith( '.' ) )
4085 ext.remove( 0, 1 );
4086
4087 GDALAllRegister();
4088 int const drvCount = GDALGetDriverCount();
4089
4090 for ( int i = 0; i < drvCount; ++i )
4091 {
4092 GDALDriverH drv = GDALGetDriver( i );
4093 if ( drv )
4094 {
4095 char **driverMetadata = GDALGetMetadata( drv, nullptr );
4096 if ( CSLFetchBoolean( driverMetadata, GDAL_DCAP_CREATE, false ) && CSLFetchBoolean( driverMetadata, GDAL_DCAP_VECTOR, false ) )
4097 {
4098 QString drvName = GDALGetDriverShortName( drv );
4099 QStringList driverExtensions = QString( GDALGetMetadataItem( drv, GDAL_DMD_EXTENSIONS, nullptr ) ).split( ' ' );
4100
4101 const auto constDriverExtensions = driverExtensions;
4102 for ( const QString &driver : constDriverExtensions )
4103 {
4104 if ( driver.compare( ext, Qt::CaseInsensitive ) == 0 )
4105 return drvName;
4106 }
4107 }
4108 }
4109 }
4110 return QString();
4111}
4112
4114{
4115 QString filterString;
4116 const auto driverFormats = supportedFiltersAndFormats( options );
4117 for ( const FilterFormatDetails &details : driverFormats )
4118 {
4119 if ( !filterString.isEmpty() )
4120 filterString += QLatin1String( ";;" );
4121
4122 filterString += details.filterString;
4123 }
4124 return filterString;
4125}
4126
4127QString QgsVectorFileWriter::filterForDriver( const QString &driverName )
4128{
4129 MetaData metadata;
4130 if ( !driverMetadata( driverName, metadata ) || metadata.trLongName.isEmpty() || metadata.glob.isEmpty() )
4131 return QString();
4132
4133 return QStringLiteral( "%1 (%2 %3)" ).arg( metadata.trLongName,
4134 metadata.glob.toLower(),
4135 metadata.glob.toUpper() );
4136}
4137
4139{
4140 if ( codecName == QLatin1String( "System" ) )
4141 return QStringLiteral( "LDID/0" );
4142
4143 const thread_local QRegularExpression re( QRegularExpression::anchoredPattern( QString( "(CP|windows-|ISO[ -])(.+)" ) ), QRegularExpression::CaseInsensitiveOption );
4144 const QRegularExpressionMatch match = re.match( codecName );
4145 if ( match.hasMatch() )
4146 {
4147 QString c = match.captured( 2 ).remove( '-' );
4148 bool isNumber;
4149 ( void ) c.toInt( &isNumber );
4150 if ( isNumber )
4151 return c;
4152 }
4153 return codecName;
4154}
4155
4156void QgsVectorFileWriter::createSymbolLayerTable( QgsVectorLayer *vl, const QgsCoordinateTransform &ct, OGRDataSourceH ds )
4157{
4158 if ( !vl || !ds )
4159 {
4160 return;
4161 }
4162
4163 QgsFeatureRenderer *renderer = vl->renderer();
4164 if ( !renderer )
4165 {
4166 return;
4167 }
4168
4169 //unit type
4170 Qgis::DistanceUnit mapUnits = vl->crs().mapUnits();
4171 if ( ct.isValid() )
4172 {
4173 mapUnits = ct.destinationCrs().mapUnits();
4174 }
4175
4176 mSymbolLayerTable.clear();
4177 OGRStyleTableH ogrStyleTable = OGR_STBL_Create();
4178 OGRStyleMgrH styleManager = OGR_SM_Create( ogrStyleTable );
4179
4180 //get symbols
4181 int nTotalLevels = 0;
4182 QgsSymbolList symbolList = renderer->symbols( mRenderContext );
4183 QgsSymbolList::iterator symbolIt = symbolList.begin();
4184 for ( ; symbolIt != symbolList.end(); ++symbolIt )
4185 {
4186 double mmsf = mmScaleFactor( mSymbologyScale, ( *symbolIt )->outputUnit(), mapUnits );
4187 double musf = mapUnitScaleFactor( mSymbologyScale, ( *symbolIt )->outputUnit(), mapUnits );
4188
4189 int nLevels = ( *symbolIt )->symbolLayerCount();
4190 for ( int i = 0; i < nLevels; ++i )
4191 {
4192 mSymbolLayerTable.insert( ( *symbolIt )->symbolLayer( i ), QString::number( nTotalLevels ) );
4193 OGR_SM_AddStyle( styleManager, QString::number( nTotalLevels ).toLocal8Bit(),
4194 ( *symbolIt )->symbolLayer( i )->ogrFeatureStyle( mmsf, musf ).toLocal8Bit() );
4195 ++nTotalLevels;
4196 }
4197 }
4198 OGR_DS_SetStyleTableDirectly( ds, ogrStyleTable );
4199}
4200
4201QgsVectorFileWriter::WriterError QgsVectorFileWriter::exportFeaturesSymbolLevels( const PreparedWriterDetails &details, QgsFeatureIterator &fit,
4202 const QgsCoordinateTransform &ct, QString *errorMessage )
4203{
4204 if ( !details.renderer )
4205 return ErrInvalidLayer;
4206
4207 mRenderContext.expressionContext() = details.expressionContext;
4208
4209 QHash< QgsSymbol *, QList<QgsFeature> > features;
4210
4211 //unit type
4212 Qgis::DistanceUnit mapUnits = details.sourceCrs.mapUnits();
4213 if ( ct.isValid() )
4214 {
4215 mapUnits = ct.destinationCrs().mapUnits();
4216 }
4217
4218 startRender( details.renderer.get(), details.sourceFields );
4219
4220 //fetch features
4221 QgsFeature fet;
4222 QgsSymbol *featureSymbol = nullptr;
4223 while ( fit.nextFeature( fet ) )
4224 {
4225 if ( ct.isValid() )
4226 {
4227 try
4228 {
4229 if ( fet.hasGeometry() )
4230 {
4231 QgsGeometry g = fet.geometry();
4232 g.transform( ct );
4233 fet.setGeometry( g );
4234 }
4235 }
4236 catch ( QgsCsException &e )
4237 {
4238 QString msg = QObject::tr( "Failed to transform, writing stopped. (Exception: %1)" )
4239 .arg( e.what() );
4240 QgsLogger::warning( msg );
4241 if ( errorMessage )
4242 *errorMessage = msg;
4243
4244 return ErrProjection;
4245 }
4246 }
4247 mRenderContext.expressionContext().setFeature( fet );
4248
4249 featureSymbol = mRenderer->symbolForFeature( fet, mRenderContext );
4250 if ( !featureSymbol )
4251 {
4252 continue;
4253 }
4254
4255 QHash< QgsSymbol *, QList<QgsFeature> >::iterator it = features.find( featureSymbol );
4256 if ( it == features.end() )
4257 {
4258 it = features.insert( featureSymbol, QList<QgsFeature>() );
4259 }
4260 it.value().append( fet );
4261 }
4262
4263 //find out order
4264 QgsSymbolLevelOrder levels;
4265 QgsSymbolList symbols = mRenderer->symbols( mRenderContext );
4266 for ( int i = 0; i < symbols.count(); i++ )
4267 {
4268 QgsSymbol *sym = symbols[i];
4269 for ( int j = 0; j < sym->symbolLayerCount(); j++ )
4270 {
4271 int level = sym->symbolLayer( j )->renderingPass();
4272 if ( level < 0 || level >= 1000 ) // ignore invalid levels
4273 continue;
4274 QgsSymbolLevelItem item( sym, j );
4275 while ( level >= levels.count() ) // append new empty levels
4276 levels.append( QgsSymbolLevel() );
4277 levels[level].append( item );
4278 }
4279 }
4280
4281 int nErrors = 0;
4282 int nTotalFeatures = 0;
4283
4284 //export symbol layers and symbology
4285 for ( int l = 0; l < levels.count(); l++ )
4286 {
4287 QgsSymbolLevel &level = levels[l];
4288 for ( int i = 0; i < level.count(); i++ )
4289 {
4290 QgsSymbolLevelItem &item = level[i];
4291 QHash< QgsSymbol *, QList<QgsFeature> >::iterator levelIt = features.find( item.symbol() );
4292 if ( levelIt == features.end() )
4293 {
4294 ++nErrors;
4295 continue;
4296 }
4297
4298 double mmsf = mmScaleFactor( mSymbologyScale, levelIt.key()->outputUnit(), mapUnits );
4299 double musf = mapUnitScaleFactor( mSymbologyScale, levelIt.key()->outputUnit(), mapUnits );
4300
4301 int llayer = item.layer();
4302 QList<QgsFeature> &featureList = levelIt.value();
4303 QList<QgsFeature>::iterator featureIt = featureList.begin();
4304 for ( ; featureIt != featureList.end(); ++featureIt )
4305 {
4306 ++nTotalFeatures;
4307 gdal::ogr_feature_unique_ptr ogrFeature = createFeature( *featureIt );
4308 if ( !ogrFeature )
4309 {
4310 ++nErrors;
4311 continue;
4312 }
4313
4314 QString styleString = levelIt.key()->symbolLayer( llayer )->ogrFeatureStyle( mmsf, musf );
4315 if ( !styleString.isEmpty() )
4316 {
4317 OGR_F_SetStyleString( ogrFeature.get(), styleString.toLocal8Bit().constData() );
4318 if ( !writeFeature( mLayer, ogrFeature.get() ) )
4319 {
4320 ++nErrors;
4321 }
4322 }
4323 }
4324 }
4325 }
4326
4327 stopRender();
4328
4329 if ( nErrors > 0 && errorMessage )
4330 {
4331 *errorMessage += QObject::tr( "\nOnly %1 of %2 features written." ).arg( nTotalFeatures - nErrors ).arg( nTotalFeatures );
4332 }
4333
4335}
4336
4337double QgsVectorFileWriter::mmScaleFactor( double scale, Qgis::RenderUnit symbolUnits, Qgis::DistanceUnit mapUnits )
4338{
4339 if ( symbolUnits == Qgis::RenderUnit::Millimeters )
4340 {
4341 return 1.0;
4342 }
4343 else
4344 {
4345 //conversion factor map units -> mm
4346 if ( mapUnits == Qgis::DistanceUnit::Meters )
4347 {
4348 return 1000 / scale;
4349 }
4350
4351 }
4352 return 1.0; //todo: map units
4353}
4354
4355double QgsVectorFileWriter::mapUnitScaleFactor( double scale, Qgis::RenderUnit symbolUnits, Qgis::DistanceUnit mapUnits )
4356{
4357 if ( symbolUnits == Qgis::RenderUnit::MapUnits )
4358 {
4359 return 1.0;
4360 }
4361 else
4362 {
4363 if ( symbolUnits == Qgis::RenderUnit::Millimeters && mapUnits == Qgis::DistanceUnit::Meters )
4364 {
4365 return scale / 1000;
4366 }
4367 }
4368 return 1.0;
4369}
4370
4371void QgsVectorFileWriter::startRender( QgsFeatureRenderer *sourceRenderer, const QgsFields &fields )
4372{
4373 mRenderer = createSymbologyRenderer( sourceRenderer );
4374 if ( !mRenderer )
4375 {
4376 return;
4377 }
4378
4379 mRenderer->startRender( mRenderContext, fields );
4380}
4381
4382void QgsVectorFileWriter::stopRender()
4383{
4384 if ( !mRenderer )
4385 {
4386 return;
4387 }
4388
4389 mRenderer->stopRender( mRenderContext );
4390}
4391
4392std::unique_ptr<QgsFeatureRenderer> QgsVectorFileWriter::createSymbologyRenderer( QgsFeatureRenderer *sourceRenderer ) const
4393{
4394 switch ( mSymbologyExport )
4395 {
4397 {
4398 return nullptr;
4399 }
4402 break;
4403 }
4404
4405 if ( !sourceRenderer )
4406 {
4407 return nullptr;
4408 }
4409
4410 return std::unique_ptr< QgsFeatureRenderer >( sourceRenderer->clone() );
4411}
4412
4413void QgsVectorFileWriter::addRendererAttributes( QgsFeatureRenderer *renderer, QgsRenderContext &context, const QgsFields &fields, QgsAttributeList &attList )
4414{
4415 if ( renderer )
4416 {
4417 const QSet<QString> rendererAttributes = renderer->usedAttributes( context );
4418 for ( const QString &attr : rendererAttributes )
4419 {
4420 int index = fields.lookupField( attr );
4421 if ( index != -1 )
4422 {
4423 attList.append( index );
4424 }
4425 }
4426 }
4427}
4428
4429QStringList QgsVectorFileWriter::concatenateOptions( const QMap<QString, QgsVectorFileWriter::Option *> &options )
4430{
4431 QStringList list;
4432 QMap<QString, QgsVectorFileWriter::Option *>::ConstIterator it;
4433
4434 for ( it = options.constBegin(); it != options.constEnd(); ++it )
4435 {
4436 QgsVectorFileWriter::Option *option = it.value();
4437 switch ( option->type )
4438 {
4440 {
4441 QgsVectorFileWriter::IntOption *opt = dynamic_cast<QgsVectorFileWriter::IntOption *>( option );
4442 if ( opt )
4443 {
4444 list.append( QStringLiteral( "%1=%2" ).arg( it.key() ).arg( opt->defaultValue ) );
4445 }
4446 break;
4447 }
4448
4450 {
4451 QgsVectorFileWriter::SetOption *opt = dynamic_cast<QgsVectorFileWriter::SetOption *>( option );
4452 if ( opt && !opt->defaultValue.isEmpty() )
4453 {
4454 list.append( QStringLiteral( "%1=%2" ).arg( it.key(), opt->defaultValue ) );
4455 }
4456 break;
4457 }
4458
4460 {
4462 if ( opt && !opt->defaultValue.isNull() )
4463 {
4464 list.append( QStringLiteral( "%1=%2" ).arg( it.key(), opt->defaultValue ) );
4465 }
4466 break;
4467 }
4468
4471 if ( opt && !opt->mValue.isEmpty() )
4472 {
4473 list.append( QStringLiteral( "%1=%2" ).arg( it.key(), opt->mValue ) );
4474 }
4475 break;
4476 }
4477 }
4478
4479 return list;
4480}
4481
4483{
4484 OGRSFDriverH hDriver = nullptr;
4485 gdal::ogr_datasource_unique_ptr hDS( OGROpen( datasetName.toUtf8().constData(), TRUE, &hDriver ) );
4486 if ( !hDS )
4488 QString drvName = OGR_Dr_GetName( hDriver );
4490 if ( OGR_DS_TestCapability( hDS.get(), ODsCCreateLayer ) )
4491 {
4492 // Shapefile driver returns True for a "foo.shp" dataset name,
4493 // creating "bar.shp" new layer, but this would be a bit confusing
4494 // for the user, so pretent that it does not support that
4495 if ( !( drvName == QLatin1String( "ESRI Shapefile" ) && QFile::exists( datasetName ) ) )
4496 caps |= CanAddNewLayer;
4497 }
4498 if ( OGR_DS_TestCapability( hDS.get(), ODsCDeleteLayer ) )
4499 {
4500 caps |= CanDeleteLayer;
4501 }
4502 int layer_count = OGR_DS_GetLayerCount( hDS.get() );
4503 if ( layer_count )
4504 {
4505 OGRLayerH hLayer = OGR_DS_GetLayer( hDS.get(), 0 );
4506 if ( hLayer )
4507 {
4508 if ( OGR_L_TestCapability( hLayer, OLCSequentialWrite ) )
4509 {
4511 if ( OGR_L_TestCapability( hLayer, OLCCreateField ) )
4512 {
4514 }
4515 }
4516 }
4517 }
4518 return caps;
4519}
4520
4521bool QgsVectorFileWriter::targetLayerExists( const QString &datasetName,
4522 const QString &layerNameIn )
4523{
4524 OGRSFDriverH hDriver = nullptr;
4525 gdal::ogr_datasource_unique_ptr hDS( OGROpen( datasetName.toUtf8().constData(), TRUE, &hDriver ) );
4526 if ( !hDS )
4527 return false;
4528
4529 QString layerName( layerNameIn );
4530 if ( layerName.isEmpty() )
4531 layerName = QFileInfo( datasetName ).baseName();
4532
4533 return OGR_DS_GetLayerByName( hDS.get(), layerName.toUtf8().constData() );
4534}
4535
4536
4537bool QgsVectorFileWriter::areThereNewFieldsToCreate( const QString &datasetName,
4538 const QString &layerName,
4539 QgsVectorLayer *layer,
4540 const QgsAttributeList &attributes )
4541{
4542 OGRSFDriverH hDriver = nullptr;
4543 gdal::ogr_datasource_unique_ptr hDS( OGROpen( datasetName.toUtf8().constData(), TRUE, &hDriver ) );
4544 if ( !hDS )
4545 return false;
4546 OGRLayerH hLayer = OGR_DS_GetLayerByName( hDS.get(), layerName.toUtf8().constData() );
4547 if ( !hLayer )
4548 {
4549 return false;
4550 }
4551 bool ret = false;
4552 OGRFeatureDefnH defn = OGR_L_GetLayerDefn( hLayer );
4553 const auto constAttributes = attributes;
4554 for ( int idx : constAttributes )
4555 {
4556 QgsField fld = layer->fields().at( idx );
4557 if ( OGR_FD_GetFieldIndex( defn, fld.name().toUtf8().constData() ) < 0 )
4558 {
4559 ret = true;
4560 break;
4561 }
4562 }
4563 return ret;
4564}
@ FieldComments
Writer can support field comments.
@ FieldAliases
Writer can support field aliases.
DistanceUnit
Units of distance.
Definition qgis.h:4625
QFlags< VectorFileWriterCapability > VectorFileWriterCapabilities
Capabilities supported by a QgsVectorFileWriter object.
Definition qgis.h:1014
RenderUnit
Rendering size units.
Definition qgis.h:4795
@ Millimeters
Millimeters.
@ MapUnits
Map units.
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:256
@ 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:5090
@ 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.
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.
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.
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
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.
Abstract base class for all 2D vector feature renderers.
virtual QgsSymbolList symbolsForFeature(const QgsFeature &feature, QgsRenderContext &context) const
Returns list of symbols used for rendering the feature.
virtual QgsSymbolList symbols(QgsRenderContext &context) const
Returns list of symbols used by the renderer.
bool usingSymbolLevels() const
virtual QgsFeatureRenderer::Capabilities capabilities()
Returns details about internals of this renderer.
virtual QSet< QString > usedAttributes(const QgsRenderContext &context) const =0
Returns a list of attributes required by this renderer.
@ SymbolLevels
Rendering with symbol levels (i.e. implements symbols(), symbolForFeature())
virtual QgsFeatureRenderer * clone() const =0
Create a deep copy of this renderer.
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:58
void initAttributes(int fieldCount)
Initialize this feature with the given number of fields.
QgsFeatureId id
Definition qgsfeature.h:66
QgsGeometry geometry
Definition qgsfeature.h:69
bool hasGeometry() const
Returns true if the feature has an associated geometry.
bool isUnsetValue(int fieldIdx) const
Returns true if the attribute at the specified index is an unset value.
Q_INVOKABLE QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:53
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition qgsfeedback.h:61
QString domainName() const
Returns the associated field domain name, for providers which support field domains.
@ ConstraintNotNull
Field may not be null.
@ ConstraintUnique
Field must have a unique value.
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:53
QMetaType::Type type
Definition qgsfield.h:60
QString typeName() const
Gets the field type.
Definition qgsfield.cpp:160
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:472
void setName(const QString &name)
Set the field name.
Definition qgsfield.cpp:226
QMetaType::Type subType() const
If the field is a collection, gets its element's type.
Definition qgsfield.cpp:155
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:46
int count
Definition qgsfields.h:50
int size() const
Returns number of items.
void clear()
Removes all fields.
Definition qgsfields.cpp:57
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Q_INVOKABLE int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
static QStringList multiLayerFileExtensions()
Returns a list of file extensions which potentially contain multiple layers representing GDAL raster ...
A geometry is the spatial representation of a feature.
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.
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:83
QgsCoordinateTransformContext transformContext() const
Returns the layer data provider coordinate transform context or a default transform context if the la...
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
Custom exception class which is raised when an operation is not supported.
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.
void changeMode(Mode mode)
Change the mode of the lock to mode.
A rectangle specified with double values.
bool isNull() const
Test if the rectangle is null (holding no spatial information).
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:231
QgsSymbolLayer * symbolLayer(int layer)
Returns the symbol layer at the specified index.
int symbolLayerCount() const
Returns the total number of symbol layers contained in the symbol.
Definition qgssymbol.h:352
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.
@ 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.
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.
static bool isMultiType(Qgis::WkbType type)
Returns true if the WKB type is a multi type.
static Qgis::WkbType addZ(Qgis::WkbType type)
Adds the z dimension to a WKB type and returns the new type.
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.
static bool hasM(Qgis::WkbType type)
Tests whether a WKB type contains m values.
static Qgis::WkbType multiType(Qgis::WkbType type)
Returns the multi type for a WKB type.
static Qgis::WkbType flatType(Qgis::WkbType type)
Returns the flat type for a WKB type.
std::unique_ptr< std::remove_pointer< OGRFeatureH >::type, OGRFeatureDeleter > ogr_feature_unique_ptr
Scoped OGR feature.
std::unique_ptr< std::remove_pointer< OGRDataSourceH >::type, OGRDataSourceDeleter > ogr_datasource_unique_ptr
Scoped OGR data source.
std::unique_ptr< std::remove_pointer< OGRFieldDefnH >::type, OGRFldDeleter > ogr_field_def_unique_ptr
Scoped OGR field definition.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
#define Q_NOWARN_DEPRECATED_POP
Definition qgis.h:6434
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:6433
QList< QgsFeature > QgsFeatureList
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:91
QList< QgsSymbolLevelItem > QgsSymbolLevel
Definition qgsrenderer.h:87
QList< QgsSymbol * > QgsSymbolList
Definition qgsrenderer.h:47
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