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