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