QGIS API Documentation 3.28.0-Firenze (ed3ad0430f)
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 geom.get()->addZValue( 0 );
2802 if ( QgsWkbTypes::hasM( mWkbType ) && !QgsWkbTypes::hasM( geom.wkbType() ) )
2803 geom.get()->addMValue( 0 );
2804
2805 if ( !mGeom2 )
2806 {
2807 // there's a problem when layer type is set as wkbtype Polygon
2808 // although there are also features of type MultiPolygon
2809 // (at least in OGR provider)
2810 // If the feature's wkbtype is different from the layer's wkbtype,
2811 // try to export it too.
2812 //
2813 // Btw. OGRGeometry must be exactly of the type of the geometry which it will receive
2814 // i.e. Polygons can't be imported to OGRMultiPolygon
2815 mGeom2 = createEmptyGeometry( geom.wkbType() );
2816 }
2817
2818 if ( !mGeom2 )
2819 {
2820 mErrorMessage = QObject::tr( "Feature geometry not imported (OGR error: %1)" )
2821 .arg( QString::fromUtf8( CPLGetLastErrorMsg() ) );
2823 QgsMessageLog::logMessage( mErrorMessage, QObject::tr( "OGR" ) );
2824 return nullptr;
2825 }
2826
2827 QByteArray wkb( geom.asWkb() );
2828 OGRErr err = OGR_G_ImportFromWkb( mGeom2, reinterpret_cast<unsigned char *>( const_cast<char *>( wkb.constData() ) ), wkb.length() );
2829 if ( err != OGRERR_NONE )
2830 {
2831 mErrorMessage = QObject::tr( "Feature geometry not imported (OGR error: %1)" )
2832 .arg( QString::fromUtf8( CPLGetLastErrorMsg() ) );
2834 QgsMessageLog::logMessage( mErrorMessage, QObject::tr( "OGR" ) );
2835 return nullptr;
2836 }
2837
2838 // pass ownership to geometry
2839 OGR_F_SetGeometryDirectly( poFeature.get(), mGeom2 );
2840 }
2841 else // wkb type matches
2842 {
2844 OGRGeometryH ogrGeom = createEmptyGeometry( mWkbType );
2845 OGRErr err = OGR_G_ImportFromWkb( ogrGeom, reinterpret_cast<unsigned char *>( const_cast<char *>( wkb.constData() ) ), wkb.length() );
2846 if ( err != OGRERR_NONE )
2847 {
2848 mErrorMessage = QObject::tr( "Feature geometry not imported (OGR error: %1)" )
2849 .arg( QString::fromUtf8( CPLGetLastErrorMsg() ) );
2851 QgsMessageLog::logMessage( mErrorMessage, QObject::tr( "OGR" ) );
2852 return nullptr;
2853 }
2854
2855 // set geometry (ownership is passed to OGR)
2856 OGR_F_SetGeometryDirectly( poFeature.get(), ogrGeom );
2857 }
2858 }
2859 else
2860 {
2861 OGR_F_SetGeometryDirectly( poFeature.get(), createEmptyGeometry( mWkbType ) );
2862 }
2863 }
2864 return poFeature;
2865}
2866
2867void QgsVectorFileWriter::resetMap( const QgsAttributeList &attributes )
2868{
2869 QMap<int, int> omap( mAttrIdxToOgrIdx );
2870 mAttrIdxToOgrIdx.clear();
2871 for ( int i = 0; i < attributes.size(); i++ )
2872 {
2873 if ( omap.find( i ) != omap.end() )
2874 mAttrIdxToOgrIdx.insert( attributes[i], omap[i] );
2875 }
2876}
2877
2878bool QgsVectorFileWriter::writeFeature( OGRLayerH layer, OGRFeatureH feature )
2879{
2880 if ( OGR_L_CreateFeature( layer, feature ) != OGRERR_NONE )
2881 {
2882 mErrorMessage = QObject::tr( "Feature creation error (OGR error: %1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) );
2884 QgsMessageLog::logMessage( mErrorMessage, QObject::tr( "OGR" ) );
2885 return false;
2886 }
2887 return true;
2888}
2889
2891{
2892 if ( mUsingTransaction )
2893 {
2894 if ( OGRERR_NONE != OGR_L_CommitTransaction( mLayer ) )
2895 {
2896 QgsDebugMsg( QStringLiteral( "Error while committing transaction on OGRLayer." ) );
2897 }
2898 }
2899 mDS.reset();
2900
2901 if ( mOgrRef )
2902 {
2903 OSRRelease( mOgrRef );
2904 }
2905}
2906
2909 const QString &fileName,
2910 const QString &fileEncoding,
2911 const QgsCoordinateReferenceSystem &destCRS,
2912 const QString &driverName,
2913 bool onlySelected,
2914 QString *errorMessage,
2915 const QStringList &datasourceOptions,
2916 const QStringList &layerOptions,
2917 bool skipAttributeCreation,
2918 QString *newFilename,
2919 SymbologyExport symbologyExport,
2920 double symbologyScale,
2921 const QgsRectangle *filterExtent,
2922 QgsWkbTypes::Type overrideGeometryType,
2923 bool forceMulti,
2924 bool includeZ,
2925 const QgsAttributeList &attributes,
2926 FieldValueConverter *fieldValueConverter,
2927 QString *newLayer )
2928{
2930 if ( destCRS.isValid() && layer )
2931 {
2932 ct = QgsCoordinateTransform( layer->crs(), destCRS, layer->transformContext() );
2933 }
2934
2935 SaveVectorOptions options;
2936 options.fileEncoding = fileEncoding;
2937 options.ct = ct;
2938 options.driverName = driverName;
2939 options.onlySelectedFeatures = onlySelected;
2940 options.datasourceOptions = datasourceOptions;
2941 options.layerOptions = layerOptions;
2942 options.skipAttributeCreation = skipAttributeCreation;
2945 if ( filterExtent )
2946 options.filterExtent = *filterExtent;
2947 options.overrideGeometryType = overrideGeometryType;
2948 options.forceMulti = forceMulti;
2949 options.includeZ = includeZ;
2950 options.attributes = attributes;
2951 options.fieldValueConverter = fieldValueConverter;
2952 return writeAsVectorFormatV3( layer, fileName, layer->transformContext(), options, errorMessage, newFilename, newLayer );
2953}
2954
2956 const QString &fileName,
2957 const QString &fileEncoding,
2958 const QgsCoordinateTransform &ct,
2959 const QString &driverName,
2960 bool onlySelected,
2961 QString *errorMessage,
2962 const QStringList &datasourceOptions,
2963 const QStringList &layerOptions,
2964 bool skipAttributeCreation,
2965 QString *newFilename,
2966 SymbologyExport symbologyExport,
2967 double symbologyScale,
2968 const QgsRectangle *filterExtent,
2969 QgsWkbTypes::Type overrideGeometryType,
2970 bool forceMulti,
2971 bool includeZ,
2972 const QgsAttributeList &attributes,
2973 FieldValueConverter *fieldValueConverter,
2974 QString *newLayer )
2975{
2976 SaveVectorOptions options;
2977 options.fileEncoding = fileEncoding;
2978 options.ct = ct;
2979 options.driverName = driverName;
2980 options.onlySelectedFeatures = onlySelected;
2981 options.datasourceOptions = datasourceOptions;
2982 options.layerOptions = layerOptions;
2983 options.skipAttributeCreation = skipAttributeCreation;
2986 if ( filterExtent )
2987 options.filterExtent = *filterExtent;
2988 options.overrideGeometryType = overrideGeometryType;
2989 options.forceMulti = forceMulti;
2990 options.includeZ = includeZ;
2991 options.attributes = attributes;
2992 options.fieldValueConverter = fieldValueConverter;
2993 return writeAsVectorFormatV3( layer, fileName, layer->transformContext(), options, errorMessage, newFilename, newLayer );
2994}
2995
2996
2998 : driverName( QStringLiteral( "GPKG" ) )
2999{
3000}
3001
3002
3003
3004QgsVectorFileWriter::WriterError QgsVectorFileWriter::prepareWriteAsVectorFormat( QgsVectorLayer *layer, const QgsVectorFileWriter::SaveVectorOptions &options, QgsVectorFileWriter::PreparedWriterDetails &details )
3005{
3006 if ( !layer || !layer->isValid() )
3007 {
3008 return ErrInvalidLayer;
3009 }
3010
3011 if ( layer->renderer() )
3012 details.renderer.reset( layer->renderer()->clone() );
3013 details.sourceCrs = layer->crs();
3014 details.sourceWkbType = layer->wkbType();
3015 details.sourceFields = layer->fields();
3016 details.providerType = layer->providerType();
3017 details.featureCount = options.onlySelectedFeatures ? layer->selectedFeatureCount() : layer->featureCount();
3018 if ( layer->dataProvider() )
3019 details.dataSourceUri = layer->dataProvider()->dataSourceUri();
3020 details.storageType = layer->storageType();
3021 details.selectedFeatureIds = layer->selectedFeatureIds();
3022 details.providerUriParams = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
3023
3024 if ( details.storageType == QLatin1String( "ESRI Shapefile" ) )
3025 {
3027 if ( options.onlySelectedFeatures )
3028 {
3029 req.setFilterFids( details.selectedFeatureIds );
3030 }
3031 req.setNoAttributes();
3032 details.geometryTypeScanIterator = layer->getFeatures( req );
3033 }
3034
3035 details.expressionContext = QgsExpressionContext( QgsExpressionContextUtils::globalProjectLayerScopes( layer ) );
3036 details.renderContext.setExpressionContext( details.expressionContext );
3037 details.renderContext.setRendererScale( options.symbologyScale );
3038
3039 details.shallTransform = false;
3040 if ( options.ct.isValid() )
3041 {
3042 // This means we should transform
3043 details.outputCrs = options.ct.destinationCrs();
3044 details.shallTransform = true;
3045 }
3046 else
3047 {
3048 // This means we shouldn't transform, use source CRS as output (if defined)
3049 details.outputCrs = details.sourceCrs;
3050 }
3051
3052 details.destWkbType = details.sourceWkbType;
3054 {
3055 details.destWkbType = QgsWkbTypes::flatType( options.overrideGeometryType );
3056 if ( QgsWkbTypes::hasZ( options.overrideGeometryType ) || options.includeZ )
3057 details.destWkbType = QgsWkbTypes::addZ( details.destWkbType );
3058 }
3059 if ( options.forceMulti )
3060 {
3061 details.destWkbType = QgsWkbTypes::multiType( details.destWkbType );
3062 }
3063
3064 details.attributes = options.attributes;
3065 if ( options.skipAttributeCreation )
3066 details.attributes.clear();
3067 else if ( details.attributes.isEmpty() )
3068 {
3069 const QgsAttributeList allAttributes = details.sourceFields.allAttributesList();
3070 for ( int idx : allAttributes )
3071 {
3072 QgsField fld = details.sourceFields.at( idx );
3073 if ( details.providerType == QLatin1String( "oracle" ) && fld.typeName().contains( QLatin1String( "SDO_GEOMETRY" ) ) )
3074 continue;
3075 details.attributes.append( idx );
3076 }
3077 }
3078
3079 if ( !details.attributes.isEmpty() )
3080 {
3081 for ( int attrIdx : std::as_const( details.attributes ) )
3082 {
3083 if ( details.sourceFields.exists( attrIdx ) )
3084 {
3085 QgsField field = details.sourceFields.at( attrIdx );
3086 field.setName( options.attributesExportNames.value( attrIdx, field.name() ) );
3087 details.outputFields.append( field );
3088 }
3089 else
3090 {
3091 QgsDebugMsg( QStringLiteral( "No such source field with index '%1' available." ).arg( attrIdx ) );
3092 }
3093 }
3094 }
3095
3096 // not ideal - would be nice to avoid this happening in the preparation step if possible,
3097 // but currently requires access to the layer's minimumValue/maximumValue methods
3098 if ( details.providerType == QLatin1String( "spatialite" ) )
3099 {
3100 for ( int i = 0; i < details.outputFields.size(); i++ )
3101 {
3102 if ( details.outputFields.at( i ).type() == QVariant::LongLong )
3103 {
3104 QVariant min;
3105 QVariant max;
3106 layer->minimumAndMaximumValue( i, min, max );
3107 if ( std::max( std::llabs( min.toLongLong() ), std::llabs( max.toLongLong() ) ) < std::numeric_limits<int>::max() )
3108 {
3109 details.outputFields[i].setType( QVariant::Int );
3110 }
3111 }
3112 }
3113 }
3114
3115
3116 //add possible attributes needed by renderer
3117 addRendererAttributes( details.renderer.get(), details.renderContext, details.sourceFields, details.attributes );
3118
3120 req.setSubsetOfAttributes( details.attributes );
3121 if ( options.onlySelectedFeatures )
3122 req.setFilterFids( details.selectedFeatureIds );
3123
3124 if ( !options.filterExtent.isNull() )
3125 {
3126 QgsRectangle filterRect = options.filterExtent;
3127 bool useFilterRect = true;
3128 if ( details.shallTransform )
3129 {
3130 try
3131 {
3132 // map filter rect back from destination CRS to layer CRS
3133 QgsCoordinateTransform extentTransform = options.ct;
3134 extentTransform.setBallparkTransformsAreAppropriate( true );
3135 filterRect = extentTransform.transformBoundingBox( filterRect, Qgis::TransformDirection::Reverse );
3136 }
3137 catch ( QgsCsException & )
3138 {
3139 useFilterRect = false;
3140 }
3141 }
3142 if ( useFilterRect )
3143 {
3144 req.setFilterRect( filterRect );
3145 }
3146 details.filterRectGeometry = QgsGeometry::fromRect( options.filterExtent );
3147 details.filterRectEngine.reset( QgsGeometry::createGeometryEngine( details.filterRectGeometry.constGet() ) );
3148 details.filterRectEngine->prepareGeometry();
3149 }
3150 details.sourceFeatureIterator = layer->getFeatures( req );
3151
3152 return NoError;
3153}
3154
3155QgsVectorFileWriter::WriterError QgsVectorFileWriter::writeAsVectorFormat( PreparedWriterDetails &details, const QString &fileName, const QgsVectorFileWriter::SaveVectorOptions &options, QString *newFilename, QString *errorMessage, QString *newLayer )
3156{
3157 return writeAsVectorFormatV2( details, fileName, QgsCoordinateTransformContext(), options, newFilename, newLayer, errorMessage );
3158}
3159
3160QgsVectorFileWriter::WriterError QgsVectorFileWriter::writeAsVectorFormatV2( PreparedWriterDetails &details, const QString &fileName, const QgsCoordinateTransformContext &transformContext, const QgsVectorFileWriter::SaveVectorOptions &options, QString *newFilename, QString *newLayer, QString *errorMessage )
3161{
3162 QgsWkbTypes::Type destWkbType = details.destWkbType;
3163
3164 int lastProgressReport = 0;
3165 long long total = details.featureCount;
3166
3167 // Special rules for OGR layers
3168 if ( details.providerType == QLatin1String( "ogr" ) && !details.dataSourceUri.isEmpty() )
3169 {
3170 QString srcFileName( details.providerUriParams.value( QStringLiteral( "path" ) ).toString() );
3171 if ( QFile::exists( srcFileName ) && QFileInfo( fileName ).canonicalFilePath() == QFileInfo( srcFileName ).canonicalFilePath() )
3172 {
3173 // Check the layer name too if it's a GPKG/SpatiaLite/SQLite OGR driver (pay attention: camel case in layerName)
3174 QgsDataSourceUri uri( details.dataSourceUri );
3175 if ( !( ( options.driverName == QLatin1String( "GPKG" ) ||
3176 options.driverName == QLatin1String( "SpatiaLite" ) ||
3177 options.driverName == QLatin1String( "SQLite" ) ) &&
3178 options.layerName != details.providerUriParams.value( QStringLiteral( "layerName" ) ) ) )
3179 {
3180 if ( errorMessage )
3181 *errorMessage = QObject::tr( "Cannot overwrite a OGR layer in place" );
3182 return ErrCreateDataSource;
3183 }
3184 }
3185
3186 // Shapefiles might contain multi types although wkbType() only reports singles
3187 if ( details.storageType == QLatin1String( "ESRI Shapefile" ) && !QgsWkbTypes::isMultiType( destWkbType ) )
3188 {
3189 QgsFeatureIterator fit = details.geometryTypeScanIterator;
3190 QgsFeature fet;
3191 long scanned = 0;
3192 while ( fit.nextFeature( fet ) )
3193 {
3194 if ( options.feedback && options.feedback->isCanceled() )
3195 {
3196 return Canceled;
3197 }
3198 if ( options.feedback )
3199 {
3200 //dedicate first 5% of progress bar to this scan
3201 int newProgress = static_cast<int>( ( 5.0 * scanned ) / total );
3202 if ( newProgress != lastProgressReport )
3203 {
3204 lastProgressReport = newProgress;
3205 options.feedback->setProgress( lastProgressReport );
3206 }
3207 }
3208
3209 if ( fet.hasGeometry() && QgsWkbTypes::isMultiType( fet.geometry().wkbType() ) )
3210 {
3211 destWkbType = QgsWkbTypes::multiType( destWkbType );
3212 break;
3213 }
3214 scanned++;
3215 }
3216 }
3217 }
3218
3219 QString tempNewFilename;
3220 QString tempNewLayer;
3221
3222 std::unique_ptr< QgsVectorFileWriter > writer( create( fileName, details.outputFields, destWkbType, details.outputCrs, transformContext, options, QgsFeatureSink::SinkFlags(), &tempNewFilename, &tempNewLayer ) );
3223 writer->setSymbologyScale( options.symbologyScale );
3224
3225 if ( newFilename )
3226 *newFilename = tempNewFilename;
3227
3228 if ( newLayer )
3229 *newLayer = tempNewLayer;
3230
3231 if ( newFilename )
3232 {
3233 QgsDebugMsgLevel( "newFilename = " + *newFilename, 2 );
3234 }
3235
3236 // check whether file creation was successful
3237 WriterError err = writer->hasError();
3238 if ( err != NoError )
3239 {
3240 if ( errorMessage )
3241 *errorMessage = writer->errorMessage();
3242 return err;
3243 }
3244
3245 if ( errorMessage )
3246 {
3247 errorMessage->clear();
3248 }
3249
3250 QgsFeature fet;
3251
3252 //create symbol table if needed
3253 if ( writer->symbologyExport() != NoSymbology )
3254 {
3255 //writer->createSymbolLayerTable( layer, writer->mDS );
3256 }
3257
3258 if ( writer->symbologyExport() == SymbolLayerSymbology )
3259 {
3260 QgsFeatureRenderer *r = details.renderer.get();
3262 && r->usingSymbolLevels() )
3263 {
3264 QgsVectorFileWriter::WriterError error = writer->exportFeaturesSymbolLevels( details, details.sourceFeatureIterator, options.ct, errorMessage );
3265 return ( error == NoError ) ? NoError : ErrFeatureWriteFailed;
3266 }
3267 }
3268
3269 int n = 0, errors = 0;
3270
3271 //unit type
3272 QgsUnitTypes::DistanceUnit mapUnits = details.sourceCrs.mapUnits();
3273 if ( options.ct.isValid() )
3274 {
3275 mapUnits = options.ct.destinationCrs().mapUnits();
3276 }
3277
3278 writer->startRender( details.renderer.get(), details.sourceFields );
3279
3280 writer->resetMap( details.attributes );
3281 // Reset mFields to layer fields, and not just exported fields
3282 writer->mFields = details.sourceFields;
3283
3284 // write all features
3285 long saved = 0;
3286 int initialProgress = lastProgressReport;
3287 while ( details.sourceFeatureIterator.nextFeature( fet ) )
3288 {
3289 if ( options.feedback && options.feedback->isCanceled() )
3290 {
3291 return Canceled;
3292 }
3293
3294 saved++;
3295 if ( options.feedback )
3296 {
3297 //avoid spamming progress reports
3298 int newProgress = static_cast<int>( initialProgress + ( ( 100.0 - initialProgress ) * saved ) / total );
3299 if ( newProgress < 100 && newProgress != lastProgressReport )
3300 {
3301 lastProgressReport = newProgress;
3302 options.feedback->setProgress( lastProgressReport );
3303 }
3304 }
3305
3306 if ( details.shallTransform )
3307 {
3308 try
3309 {
3310 if ( fet.hasGeometry() )
3311 {
3312 QgsGeometry g = fet.geometry();
3313 g.transform( options.ct );
3314 fet.setGeometry( g );
3315 }
3316 }
3317 catch ( QgsCsException &e )
3318 {
3319 QString msg = QObject::tr( "Failed to transform a point while drawing a feature with ID '%1'. Writing stopped. (Exception: %2)" )
3320 .arg( fet.id() ).arg( e.what() );
3321 QgsLogger::warning( msg );
3322 if ( errorMessage )
3323 *errorMessage = msg;
3324
3325 return ErrProjection;
3326 }
3327 }
3328
3329 if ( fet.hasGeometry() && details.filterRectEngine && !details.filterRectEngine->intersects( fet.geometry().constGet() ) )
3330 continue;
3331
3332 if ( details.attributes.empty() && options.skipAttributeCreation )
3333 {
3334 fet.initAttributes( 0 );
3335 }
3336
3337 if ( !writer->addFeatureWithStyle( fet, writer->mRenderer.get(), mapUnits ) )
3338 {
3339 WriterError err = writer->hasError();
3340 if ( err != NoError && errorMessage )
3341 {
3342 if ( errorMessage->isEmpty() )
3343 {
3344 *errorMessage = QObject::tr( "Feature write errors:" );
3345 }
3346 *errorMessage += '\n' + writer->errorMessage();
3347 }
3348 errors++;
3349
3350 if ( errors > 1000 )
3351 {
3352 if ( errorMessage )
3353 {
3354 *errorMessage += QObject::tr( "Stopping after %n error(s)", nullptr, errors );
3355 }
3356
3357 n = -1;
3358 break;
3359 }
3360 }
3361 n++;
3362 }
3363
3364 writer->stopRender();
3365
3366 if ( errors > 0 && errorMessage && n > 0 )
3367 {
3368 *errorMessage += QObject::tr( "\nOnly %1 of %2 features written." ).arg( n - errors ).arg( n );
3369 }
3370
3371 writer.reset();
3372
3373 bool metadataFailure = false;
3374 if ( options.saveMetadata )
3375 {
3376 QString uri = QgsProviderRegistry::instance()->encodeUri( QStringLiteral( "ogr" ), QVariantMap
3377 {
3378 {QStringLiteral( "path" ), tempNewFilename },
3379 {QStringLiteral( "layerName" ), tempNewLayer }
3380 } );
3381
3382 try
3383 {
3384 QString error;
3385 if ( !QgsProviderRegistry::instance()->saveLayerMetadata( QStringLiteral( "ogr" ), uri, options.layerMetadata, error ) )
3386 {
3387 if ( errorMessage )
3388 {
3389 if ( !errorMessage->isEmpty() )
3390 *errorMessage += '\n';
3391 *errorMessage += error;
3392 }
3393 metadataFailure = true;
3394 }
3395 }
3396 catch ( QgsNotSupportedException &e )
3397 {
3398 if ( errorMessage )
3399 {
3400 if ( !errorMessage->isEmpty() )
3401 *errorMessage += '\n';
3402 *errorMessage += e.what();
3403 }
3404 metadataFailure = true;
3405 }
3406 }
3407
3408 return errors == 0 ? ( !metadataFailure ? NoError : ErrSavingMetadata ) : ErrFeatureWriteFailed;
3409}
3410
3412 const QString &fileName,
3413 const SaveVectorOptions &options,
3414 QString *newFilename,
3415 QString *errorMessage,
3416 QString *newLayer )
3417{
3418 QgsVectorFileWriter::PreparedWriterDetails details;
3419 WriterError err = prepareWriteAsVectorFormat( layer, options, details );
3420 if ( err != NoError )
3421 return err;
3422
3423 return writeAsVectorFormatV2( details, fileName, layer->transformContext(), options, newFilename, newLayer, errorMessage );
3424}
3425
3427 const QString &fileName,
3428 const QgsCoordinateTransformContext &transformContext,
3429 const SaveVectorOptions &options,
3430 QString *newFilename,
3431 QString *newLayer,
3432 QString *errorMessage )
3433{
3434 QgsVectorFileWriter::PreparedWriterDetails details;
3435 WriterError err = prepareWriteAsVectorFormat( layer, options, details );
3436 if ( err != NoError )
3437 return err;
3438
3439 return writeAsVectorFormatV2( details, fileName, transformContext, options, errorMessage, newFilename, newLayer );
3440}
3441
3442QgsVectorFileWriter::WriterError QgsVectorFileWriter::writeAsVectorFormatV3( QgsVectorLayer *layer, const QString &fileName, const QgsCoordinateTransformContext &transformContext, const QgsVectorFileWriter::SaveVectorOptions &options, QString *errorMessage, QString *newFilename, QString *newLayer )
3443{
3444 QgsVectorFileWriter::PreparedWriterDetails details;
3445 WriterError err = prepareWriteAsVectorFormat( layer, options, details );
3446 if ( err != NoError )
3447 return err;
3448
3449 return writeAsVectorFormatV2( details, fileName, transformContext, options, newFilename, newLayer, errorMessage );
3450}
3451
3452bool QgsVectorFileWriter::deleteShapeFile( const QString &fileName )
3453{
3454 QFileInfo fi( fileName );
3455 QDir dir = fi.dir();
3456
3457 QStringList filter;
3458 for ( const char *suffix : { ".shp", ".shx", ".dbf", ".prj", ".qix", ".qpj", ".cpg", ".sbn", ".sbx", ".idm", ".ind" } )
3459 {
3460 filter << fi.completeBaseName() + suffix;
3461 }
3462
3463 bool ok = true;
3464 const auto constEntryList = dir.entryList( filter );
3465 for ( const QString &file : constEntryList )
3466 {
3467 QFile f( dir.canonicalPath() + '/' + file );
3468 if ( !f.remove() )
3469 {
3470 QgsDebugMsg( QStringLiteral( "Removing file %1 failed: %2" ).arg( file, f.errorString() ) );
3471 ok = false;
3472 }
3473 }
3474
3475 return ok;
3476}
3477
3479{
3480 mSymbologyScale = d;
3481 mRenderContext.setRendererScale( mSymbologyScale );
3482}
3483
3484QList< QgsVectorFileWriter::FilterFormatDetails > QgsVectorFileWriter::supportedFiltersAndFormats( const VectorFormatOptions options )
3485{
3486 static QReadWriteLock sFilterLock;
3487 static QMap< VectorFormatOptions, QList< QgsVectorFileWriter::FilterFormatDetails > > sFilters;
3488
3489 QgsReadWriteLocker locker( sFilterLock, QgsReadWriteLocker::Read );
3490
3491 const auto it = sFilters.constFind( options );
3492 if ( it != sFilters.constEnd() )
3493 return it.value();
3494
3496 QList< QgsVectorFileWriter::FilterFormatDetails > results;
3497
3499 int const drvCount = OGRGetDriverCount();
3500
3501 for ( int i = 0; i < drvCount; ++i )
3502 {
3503 OGRSFDriverH drv = OGRGetDriver( i );
3504 if ( drv )
3505 {
3506 QString drvName = OGR_Dr_GetName( drv );
3507
3508 GDALDriverH gdalDriver = GDALGetDriverByName( drvName.toLocal8Bit().constData() );
3509 char **metadata = nullptr;
3510 if ( gdalDriver )
3511 {
3512 metadata = GDALGetMetadata( gdalDriver, nullptr );
3513 }
3514
3515 bool nonSpatialFormat = CSLFetchBoolean( metadata, GDAL_DCAP_NONSPATIAL, false );
3516
3517 if ( OGR_Dr_TestCapability( drv, "CreateDataSource" ) != 0 )
3518 {
3519 if ( options & SkipNonSpatialFormats )
3520 {
3521 // skip non-spatial formats
3522 if ( nonSpatialFormat )
3523 continue;
3524 }
3525
3526 QString filterString = filterForDriver( drvName );
3527 if ( filterString.isEmpty() )
3528 continue;
3529
3530 MetaData metadata;
3531 QStringList globs;
3532 if ( driverMetadata( drvName, metadata ) && !metadata.glob.isEmpty() )
3533 {
3534 globs = metadata.glob.toLower().split( ' ' );
3535 }
3536
3537 FilterFormatDetails details;
3538 details.driverName = drvName;
3539 details.filterString = filterString;
3540 details.globs = globs;
3541
3542 results << details;
3543 }
3544 }
3545 }
3546
3547 std::sort( results.begin(), results.end(), [options]( const FilterFormatDetails & a, const FilterFormatDetails & b ) -> bool
3548 {
3549 if ( options & SortRecommended )
3550 {
3551 if ( a.driverName == QLatin1String( "GPKG" ) )
3552 return true; // Make https://twitter.com/shapefiIe a sad little fellow
3553 else if ( b.driverName == QLatin1String( "GPKG" ) )
3554 return false;
3555 else if ( a.driverName == QLatin1String( "ESRI Shapefile" ) )
3556 return true;
3557 else if ( b.driverName == QLatin1String( "ESRI Shapefile" ) )
3558 return false;
3559 }
3560
3561 return a.filterString.toLower().localeAwareCompare( b.filterString.toLower() ) < 0;
3562 } );
3563
3564 sFilters.insert( options, results );
3565 return results;
3566}
3567
3568QStringList QgsVectorFileWriter::supportedFormatExtensions( const VectorFormatOptions options )
3569{
3570 const auto formats = supportedFiltersAndFormats( options );
3571 QSet< QString > extensions;
3572
3573 const QRegularExpression rx( QStringLiteral( "\\*\\.(.*)$" ) );
3574
3575 for ( const FilterFormatDetails &format : formats )
3576 {
3577 for ( const QString &glob : format.globs )
3578 {
3579 const QRegularExpressionMatch match = rx.match( glob );
3580 if ( !match.hasMatch() )
3581 continue;
3582
3583 const QString matched = match.captured( 1 );
3584 extensions.insert( matched );
3585 }
3586 }
3587
3588 QStringList extensionList( extensions.constBegin(), extensions.constEnd() );
3589
3590 std::sort( extensionList.begin(), extensionList.end(), [options]( const QString & a, const QString & b ) -> bool
3591 {
3592 if ( options & SortRecommended )
3593 {
3594 if ( a == QLatin1String( "gpkg" ) )
3595 return true; // Make https://twitter.com/shapefiIe a sad little fellow
3596 else if ( b == QLatin1String( "gpkg" ) )
3597 return false;
3598 else if ( a == QLatin1String( "shp" ) )
3599 return true;
3600 else if ( b == QLatin1String( "shp" ) )
3601 return false;
3602 }
3603
3604 return a.toLower().localeAwareCompare( b.toLower() ) < 0;
3605 } );
3606
3607 return extensionList;
3608}
3609
3610QList< QgsVectorFileWriter::DriverDetails > QgsVectorFileWriter::ogrDriverList( const VectorFormatOptions options )
3611{
3612 QList< QgsVectorFileWriter::DriverDetails > results;
3613
3615 const int drvCount = OGRGetDriverCount();
3616
3617 QStringList writableDrivers;
3618 for ( int i = 0; i < drvCount; ++i )
3619 {
3620 OGRSFDriverH drv = OGRGetDriver( i );
3621 if ( drv )
3622 {
3623 QString drvName = OGR_Dr_GetName( drv );
3624
3625 if ( options & SkipNonSpatialFormats )
3626 {
3627 // skip non-spatial formats
3628 // TODO - use GDAL metadata to determine this, when support exists in GDAL
3629 if ( drvName == QLatin1String( "ODS" ) || drvName == QLatin1String( "XLSX" ) || drvName == QLatin1String( "XLS" ) )
3630 continue;
3631 }
3632
3633 if ( drvName == QLatin1String( "ESRI Shapefile" ) )
3634 {
3635 writableDrivers << QStringLiteral( "DBF file" );
3636 }
3637 if ( OGR_Dr_TestCapability( drv, "CreateDataSource" ) != 0 )
3638 {
3639 // Add separate format for Mapinfo MIF (MITAB is OGR default)
3640 if ( drvName == QLatin1String( "MapInfo File" ) )
3641 {
3642 writableDrivers << QStringLiteral( "MapInfo MIF" );
3643 }
3644 else if ( drvName == QLatin1String( "SQLite" ) )
3645 {
3646 // Unfortunately it seems that there is no simple way to detect if
3647 // OGR SQLite driver is compiled with SpatiaLite support.
3648 // We have HAVE_SPATIALITE in QGIS, but that may differ from OGR
3649 // http://lists.osgeo.org/pipermail/gdal-dev/2012-November/034580.html
3650 // -> test if creation fails
3651 QString option = QStringLiteral( "SPATIALITE=YES" );
3652 char *options[2] = { CPLStrdup( option.toLocal8Bit().constData() ), nullptr };
3653 OGRSFDriverH poDriver;
3655 poDriver = OGRGetDriverByName( drvName.toLocal8Bit().constData() );
3656 if ( poDriver )
3657 {
3658 gdal::ogr_datasource_unique_ptr ds( OGR_Dr_CreateDataSource( poDriver, QStringLiteral( "/vsimem/spatialitetest.sqlite" ).toUtf8().constData(), options ) );
3659 if ( ds )
3660 {
3661 writableDrivers << QStringLiteral( "SpatiaLite" );
3662 OGR_Dr_DeleteDataSource( poDriver, QStringLiteral( "/vsimem/spatialitetest.sqlite" ).toUtf8().constData() );
3663 }
3664 }
3665 CPLFree( options[0] );
3666 }
3667 writableDrivers << drvName;
3668 }
3669 }
3670 }
3671
3672 results.reserve( writableDrivers.count() );
3673 for ( const QString &drvName : std::as_const( writableDrivers ) )
3674 {
3675 MetaData metadata;
3676 if ( driverMetadata( drvName, metadata ) && !metadata.trLongName.isEmpty() )
3677 {
3678 DriverDetails details;
3679 details.driverName = drvName;
3680 details.longName = metadata.trLongName;
3681 results << details;
3682 }
3683 }
3684
3685 std::sort( results.begin(), results.end(), [options]( const DriverDetails & a, const DriverDetails & b ) -> bool
3686 {
3687 if ( options & SortRecommended )
3688 {
3689 if ( a.driverName == QLatin1String( "GPKG" ) )
3690 return true; // Make https://twitter.com/shapefiIe a sad little fellow
3691 else if ( b.driverName == QLatin1String( "GPKG" ) )
3692 return false;
3693 else if ( a.driverName == QLatin1String( "ESRI Shapefile" ) )
3694 return true;
3695 else if ( b.driverName == QLatin1String( "ESRI Shapefile" ) )
3696 return false;
3697 }
3698
3699 return a.longName.toLower().localeAwareCompare( b.longName.toLower() ) < 0;
3700 } );
3701 return results;
3702}
3703
3704QString QgsVectorFileWriter::driverForExtension( const QString &extension )
3705{
3706 QString ext = extension.trimmed();
3707 if ( ext.isEmpty() )
3708 return QString();
3709
3710 if ( ext.startsWith( '.' ) )
3711 ext.remove( 0, 1 );
3712
3713 GDALAllRegister();
3714 int const drvCount = GDALGetDriverCount();
3715
3716 for ( int i = 0; i < drvCount; ++i )
3717 {
3718 GDALDriverH drv = GDALGetDriver( i );
3719 if ( drv )
3720 {
3721 char **driverMetadata = GDALGetMetadata( drv, nullptr );
3722 if ( CSLFetchBoolean( driverMetadata, GDAL_DCAP_CREATE, false ) && CSLFetchBoolean( driverMetadata, GDAL_DCAP_VECTOR, false ) )
3723 {
3724 QString drvName = GDALGetDriverShortName( drv );
3725 QStringList driverExtensions = QString( GDALGetMetadataItem( drv, GDAL_DMD_EXTENSIONS, nullptr ) ).split( ' ' );
3726
3727 const auto constDriverExtensions = driverExtensions;
3728 for ( const QString &driver : constDriverExtensions )
3729 {
3730 if ( driver.compare( ext, Qt::CaseInsensitive ) == 0 )
3731 return drvName;
3732 }
3733 }
3734 }
3735 }
3736 return QString();
3737}
3738
3739QString QgsVectorFileWriter::fileFilterString( const VectorFormatOptions options )
3740{
3741 QString filterString;
3742 const auto driverFormats = supportedFiltersAndFormats( options );
3743 for ( const FilterFormatDetails &details : driverFormats )
3744 {
3745 if ( !filterString.isEmpty() )
3746 filterString += QLatin1String( ";;" );
3747
3748 filterString += details.filterString;
3749 }
3750 return filterString;
3751}
3752
3753QString QgsVectorFileWriter::filterForDriver( const QString &driverName )
3754{
3755 MetaData metadata;
3756 if ( !driverMetadata( driverName, metadata ) || metadata.trLongName.isEmpty() || metadata.glob.isEmpty() )
3757 return QString();
3758
3759 return QStringLiteral( "%1 (%2 %3)" ).arg( metadata.trLongName,
3760 metadata.glob.toLower(),
3761 metadata.glob.toUpper() );
3762}
3763
3765{
3766 if ( codecName == QLatin1String( "System" ) )
3767 return QStringLiteral( "LDID/0" );
3768
3769 const QRegularExpression re( QRegularExpression::anchoredPattern( QString( "(CP|windows-|ISO[ -])(.+)" ) ), QRegularExpression::CaseInsensitiveOption );
3770 const QRegularExpressionMatch match = re.match( codecName );
3771 if ( match.hasMatch() )
3772 {
3773 QString c = match.captured( 2 ).remove( '-' );
3774 bool isNumber;
3775 ( void ) c.toInt( &isNumber );
3776 if ( isNumber )
3777 return c;
3778 }
3779 return codecName;
3780}
3781
3782void QgsVectorFileWriter::createSymbolLayerTable( QgsVectorLayer *vl, const QgsCoordinateTransform &ct, OGRDataSourceH ds )
3783{
3784 if ( !vl || !ds )
3785 {
3786 return;
3787 }
3788
3789 QgsFeatureRenderer *renderer = vl->renderer();
3790 if ( !renderer )
3791 {
3792 return;
3793 }
3794
3795 //unit type
3796 QgsUnitTypes::DistanceUnit mapUnits = vl->crs().mapUnits();
3797 if ( ct.isValid() )
3798 {
3799 mapUnits = ct.destinationCrs().mapUnits();
3800 }
3801
3802 mSymbolLayerTable.clear();
3803 OGRStyleTableH ogrStyleTable = OGR_STBL_Create();
3804 OGRStyleMgrH styleManager = OGR_SM_Create( ogrStyleTable );
3805
3806 //get symbols
3807 int nTotalLevels = 0;
3808 QgsSymbolList symbolList = renderer->symbols( mRenderContext );
3809 QgsSymbolList::iterator symbolIt = symbolList.begin();
3810 for ( ; symbolIt != symbolList.end(); ++symbolIt )
3811 {
3812 double mmsf = mmScaleFactor( mSymbologyScale, ( *symbolIt )->outputUnit(), mapUnits );
3813 double musf = mapUnitScaleFactor( mSymbologyScale, ( *symbolIt )->outputUnit(), mapUnits );
3814
3815 int nLevels = ( *symbolIt )->symbolLayerCount();
3816 for ( int i = 0; i < nLevels; ++i )
3817 {
3818 mSymbolLayerTable.insert( ( *symbolIt )->symbolLayer( i ), QString::number( nTotalLevels ) );
3819 OGR_SM_AddStyle( styleManager, QString::number( nTotalLevels ).toLocal8Bit(),
3820 ( *symbolIt )->symbolLayer( i )->ogrFeatureStyle( mmsf, musf ).toLocal8Bit() );
3821 ++nTotalLevels;
3822 }
3823 }
3824 OGR_DS_SetStyleTableDirectly( ds, ogrStyleTable );
3825}
3826
3827QgsVectorFileWriter::WriterError QgsVectorFileWriter::exportFeaturesSymbolLevels( const PreparedWriterDetails &details, QgsFeatureIterator &fit,
3828 const QgsCoordinateTransform &ct, QString *errorMessage )
3829{
3830 if ( !details.renderer )
3831 return ErrInvalidLayer;
3832
3833 mRenderContext.expressionContext() = details.expressionContext;
3834
3835 QHash< QgsSymbol *, QList<QgsFeature> > features;
3836
3837 //unit type
3838 QgsUnitTypes::DistanceUnit mapUnits = details.sourceCrs.mapUnits();
3839 if ( ct.isValid() )
3840 {
3841 mapUnits = ct.destinationCrs().mapUnits();
3842 }
3843
3844 startRender( details.renderer.get(), details.sourceFields );
3845
3846 //fetch features
3847 QgsFeature fet;
3848 QgsSymbol *featureSymbol = nullptr;
3849 while ( fit.nextFeature( fet ) )
3850 {
3851 if ( ct.isValid() )
3852 {
3853 try
3854 {
3855 if ( fet.hasGeometry() )
3856 {
3857 QgsGeometry g = fet.geometry();
3858 g.transform( ct );
3859 fet.setGeometry( g );
3860 }
3861 }
3862 catch ( QgsCsException &e )
3863 {
3864 QString msg = QObject::tr( "Failed to transform, writing stopped. (Exception: %1)" )
3865 .arg( e.what() );
3866 QgsLogger::warning( msg );
3867 if ( errorMessage )
3868 *errorMessage = msg;
3869
3870 return ErrProjection;
3871 }
3872 }
3873 mRenderContext.expressionContext().setFeature( fet );
3874
3875 featureSymbol = mRenderer->symbolForFeature( fet, mRenderContext );
3876 if ( !featureSymbol )
3877 {
3878 continue;
3879 }
3880
3881 QHash< QgsSymbol *, QList<QgsFeature> >::iterator it = features.find( featureSymbol );
3882 if ( it == features.end() )
3883 {
3884 it = features.insert( featureSymbol, QList<QgsFeature>() );
3885 }
3886 it.value().append( fet );
3887 }
3888
3889 //find out order
3890 QgsSymbolLevelOrder levels;
3891 QgsSymbolList symbols = mRenderer->symbols( mRenderContext );
3892 for ( int i = 0; i < symbols.count(); i++ )
3893 {
3894 QgsSymbol *sym = symbols[i];
3895 for ( int j = 0; j < sym->symbolLayerCount(); j++ )
3896 {
3897 int level = sym->symbolLayer( j )->renderingPass();
3898 if ( level < 0 || level >= 1000 ) // ignore invalid levels
3899 continue;
3900 QgsSymbolLevelItem item( sym, j );
3901 while ( level >= levels.count() ) // append new empty levels
3902 levels.append( QgsSymbolLevel() );
3903 levels[level].append( item );
3904 }
3905 }
3906
3907 int nErrors = 0;
3908 int nTotalFeatures = 0;
3909
3910 //export symbol layers and symbology
3911 for ( int l = 0; l < levels.count(); l++ )
3912 {
3913 QgsSymbolLevel &level = levels[l];
3914 for ( int i = 0; i < level.count(); i++ )
3915 {
3916 QgsSymbolLevelItem &item = level[i];
3917 QHash< QgsSymbol *, QList<QgsFeature> >::iterator levelIt = features.find( item.symbol() );
3918 if ( levelIt == features.end() )
3919 {
3920 ++nErrors;
3921 continue;
3922 }
3923
3924 double mmsf = mmScaleFactor( mSymbologyScale, levelIt.key()->outputUnit(), mapUnits );
3925 double musf = mapUnitScaleFactor( mSymbologyScale, levelIt.key()->outputUnit(), mapUnits );
3926
3927 int llayer = item.layer();
3928 QList<QgsFeature> &featureList = levelIt.value();
3929 QList<QgsFeature>::iterator featureIt = featureList.begin();
3930 for ( ; featureIt != featureList.end(); ++featureIt )
3931 {
3932 ++nTotalFeatures;
3933 gdal::ogr_feature_unique_ptr ogrFeature = createFeature( *featureIt );
3934 if ( !ogrFeature )
3935 {
3936 ++nErrors;
3937 continue;
3938 }
3939
3940 QString styleString = levelIt.key()->symbolLayer( llayer )->ogrFeatureStyle( mmsf, musf );
3941 if ( !styleString.isEmpty() )
3942 {
3943 OGR_F_SetStyleString( ogrFeature.get(), styleString.toLocal8Bit().constData() );
3944 if ( !writeFeature( mLayer, ogrFeature.get() ) )
3945 {
3946 ++nErrors;
3947 }
3948 }
3949 }
3950 }
3951 }
3952
3953 stopRender();
3954
3955 if ( nErrors > 0 && errorMessage )
3956 {
3957 *errorMessage += QObject::tr( "\nOnly %1 of %2 features written." ).arg( nTotalFeatures - nErrors ).arg( nTotalFeatures );
3958 }
3959
3961}
3962
3963double QgsVectorFileWriter::mmScaleFactor( double scale, QgsUnitTypes::RenderUnit symbolUnits, QgsUnitTypes::DistanceUnit mapUnits )
3964{
3965 if ( symbolUnits == QgsUnitTypes::RenderMillimeters )
3966 {
3967 return 1.0;
3968 }
3969 else
3970 {
3971 //conversion factor map units -> mm
3972 if ( mapUnits == QgsUnitTypes::DistanceMeters )
3973 {
3974 return 1000 / scale;
3975 }
3976
3977 }
3978 return 1.0; //todo: map units
3979}
3980
3981double QgsVectorFileWriter::mapUnitScaleFactor( double scale, QgsUnitTypes::RenderUnit symbolUnits, QgsUnitTypes::DistanceUnit mapUnits )
3982{
3983 if ( symbolUnits == QgsUnitTypes::RenderMapUnits )
3984 {
3985 return 1.0;
3986 }
3987 else
3988 {
3989 if ( symbolUnits == QgsUnitTypes::RenderMillimeters && mapUnits == QgsUnitTypes::DistanceMeters )
3990 {
3991 return scale / 1000;
3992 }
3993 }
3994 return 1.0;
3995}
3996
3997void QgsVectorFileWriter::startRender( QgsFeatureRenderer *sourceRenderer, const QgsFields &fields )
3998{
3999 mRenderer = createSymbologyRenderer( sourceRenderer );
4000 if ( !mRenderer )
4001 {
4002 return;
4003 }
4004
4005 mRenderer->startRender( mRenderContext, fields );
4006}
4007
4008void QgsVectorFileWriter::stopRender()
4009{
4010 if ( !mRenderer )
4011 {
4012 return;
4013 }
4014
4015 mRenderer->stopRender( mRenderContext );
4016}
4017
4018std::unique_ptr<QgsFeatureRenderer> QgsVectorFileWriter::createSymbologyRenderer( QgsFeatureRenderer *sourceRenderer ) const
4019{
4021 {
4022 return nullptr;
4023 }
4024 if ( !sourceRenderer )
4025 {
4026 return nullptr;
4027 }
4028
4029 return std::unique_ptr< QgsFeatureRenderer >( sourceRenderer->clone() );
4030}
4031
4032void QgsVectorFileWriter::addRendererAttributes( QgsFeatureRenderer *renderer, QgsRenderContext &context, const QgsFields &fields, QgsAttributeList &attList )
4033{
4034 if ( renderer )
4035 {
4036 const QSet<QString> rendererAttributes = renderer->usedAttributes( context );
4037 for ( const QString &attr : rendererAttributes )
4038 {
4039 int index = fields.lookupField( attr );
4040 if ( index != -1 )
4041 {
4042 attList.append( index );
4043 }
4044 }
4045 }
4046}
4047
4048QStringList QgsVectorFileWriter::concatenateOptions( const QMap<QString, QgsVectorFileWriter::Option *> &options )
4049{
4050 QStringList list;
4051 QMap<QString, QgsVectorFileWriter::Option *>::ConstIterator it;
4052
4053 for ( it = options.constBegin(); it != options.constEnd(); ++it )
4054 {
4055 QgsVectorFileWriter::Option *option = it.value();
4056 switch ( option->type )
4057 {
4059 {
4060 QgsVectorFileWriter::IntOption *opt = dynamic_cast<QgsVectorFileWriter::IntOption *>( option );
4061 if ( opt )
4062 {
4063 list.append( QStringLiteral( "%1=%2" ).arg( it.key() ).arg( opt->defaultValue ) );
4064 }
4065 break;
4066 }
4067
4069 {
4070 QgsVectorFileWriter::SetOption *opt = dynamic_cast<QgsVectorFileWriter::SetOption *>( option );
4071 if ( opt && !opt->defaultValue.isEmpty() )
4072 {
4073 list.append( QStringLiteral( "%1=%2" ).arg( it.key(), opt->defaultValue ) );
4074 }
4075 break;
4076 }
4077
4079 {
4081 if ( opt && !opt->defaultValue.isNull() )
4082 {
4083 list.append( QStringLiteral( "%1=%2" ).arg( it.key(), opt->defaultValue ) );
4084 }
4085 break;
4086 }
4087
4090 if ( opt )
4091 {
4092 list.append( QStringLiteral( "%1=%2" ).arg( it.key(), opt->mValue ) );
4093 }
4094 break;
4095 }
4096 }
4097
4098 return list;
4099}
4100
4101QgsVectorFileWriter::EditionCapabilities QgsVectorFileWriter::editionCapabilities( const QString &datasetName )
4102{
4103 OGRSFDriverH hDriver = nullptr;
4104 gdal::ogr_datasource_unique_ptr hDS( OGROpen( datasetName.toUtf8().constData(), TRUE, &hDriver ) );
4105 if ( !hDS )
4106 return QgsVectorFileWriter::EditionCapabilities();
4107 QString drvName = OGR_Dr_GetName( hDriver );
4108 QgsVectorFileWriter::EditionCapabilities caps = QgsVectorFileWriter::EditionCapabilities();
4109 if ( OGR_DS_TestCapability( hDS.get(), ODsCCreateLayer ) )
4110 {
4111 // Shapefile driver returns True for a "foo.shp" dataset name,
4112 // creating "bar.shp" new layer, but this would be a bit confusing
4113 // for the user, so pretent that it does not support that
4114 if ( !( drvName == QLatin1String( "ESRI Shapefile" ) && QFile::exists( datasetName ) ) )
4115 caps |= CanAddNewLayer;
4116 }
4117 if ( OGR_DS_TestCapability( hDS.get(), ODsCDeleteLayer ) )
4118 {
4119 caps |= CanDeleteLayer;
4120 }
4121 int layer_count = OGR_DS_GetLayerCount( hDS.get() );
4122 if ( layer_count )
4123 {
4124 OGRLayerH hLayer = OGR_DS_GetLayer( hDS.get(), 0 );
4125 if ( hLayer )
4126 {
4127 if ( OGR_L_TestCapability( hLayer, OLCSequentialWrite ) )
4128 {
4130 if ( OGR_L_TestCapability( hLayer, OLCCreateField ) )
4131 {
4133 }
4134 }
4135 }
4136 }
4137 return caps;
4138}
4139
4140bool QgsVectorFileWriter::targetLayerExists( const QString &datasetName,
4141 const QString &layerNameIn )
4142{
4143 OGRSFDriverH hDriver = nullptr;
4144 gdal::ogr_datasource_unique_ptr hDS( OGROpen( datasetName.toUtf8().constData(), TRUE, &hDriver ) );
4145 if ( !hDS )
4146 return false;
4147
4148 QString layerName( layerNameIn );
4149 if ( layerName.isEmpty() )
4150 layerName = QFileInfo( datasetName ).baseName();
4151
4152 return OGR_DS_GetLayerByName( hDS.get(), layerName.toUtf8().constData() );
4153}
4154
4155
4156bool QgsVectorFileWriter::areThereNewFieldsToCreate( const QString &datasetName,
4157 const QString &layerName,
4158 QgsVectorLayer *layer,
4159 const QgsAttributeList &attributes )
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 OGRLayerH hLayer = OGR_DS_GetLayerByName( hDS.get(), layerName.toUtf8().constData() );
4166 if ( !hLayer )
4167 {
4168 return false;
4169 }
4170 bool ret = false;
4171 OGRFeatureDefnH defn = OGR_L_GetLayerDefn( hLayer );
4172 const auto constAttributes = attributes;
4173 for ( int idx : constAttributes )
4174 {
4175 QgsField fld = layer->fields().at( idx );
4176 if ( OGR_FD_GetFieldIndex( defn, fld.name().toUtf8().constData() ) < 0 )
4177 {
4178 ret = true;
4179 break;
4180 }
4181 }
4182 return ret;
4183}
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.
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:135
std::unique_ptr< std::remove_pointer< OGRDataSourceH >::type, OGRDataSourceDeleter > ogr_datasource_unique_ptr
Scoped OGR data source.
Definition: qgsogrutils.h:120
std::unique_ptr< std::remove_pointer< OGRFieldDefnH >::type, OGRFldDeleter > ogr_field_def_unique_ptr
Scoped OGR field definition.
Definition: qgsogrutils.h:130
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
#define FALLTHROUGH
Definition: qgis.h:3088
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:3061
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:3060
QList< QgsFeature > QgsFeatureList
Definition: qgsfeature.h:922
QList< int > QgsAttributeList
Definition: qgsfield.h:26
const QgsField & field
Definition: qgsfield.h: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