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