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