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