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