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