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