Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgsvectorlayerexporter.cpp
3 : : -------------------
4 : : begin : Thu Aug 25 2011
5 : : copyright : (C) 2011 by Giuseppe Sucameli
6 : : email : brush.tyler at gmail.com
7 : : ***************************************************************************/
8 : :
9 : : /***************************************************************************
10 : : * *
11 : : * This program is free software; you can redistribute it and/or modify *
12 : : * it under the terms of the GNU General Public License as published by *
13 : : * the Free Software Foundation; either version 2 of the License, or *
14 : : * (at your option) any later version. *
15 : : * *
16 : : ***************************************************************************/
17 : :
18 : :
19 : : #include "qgsfields.h"
20 : : #include "qgsfeature.h"
21 : : #include "qgsfeatureiterator.h"
22 : : #include "qgsgeometry.h"
23 : : #include "qgslogger.h"
24 : : #include "qgsmessagelog.h"
25 : : #include "qgsgeometrycollection.h"
26 : : #include "qgscoordinatereferencesystem.h"
27 : : #include "qgsvectorlayerexporter.h"
28 : : #include "qgsproviderregistry.h"
29 : : #include "qgsdatasourceuri.h"
30 : : #include "qgsexception.h"
31 : : #include "qgsvectordataprovider.h"
32 : : #include "qgsvectorlayer.h"
33 : : #include "qgsabstractgeometry.h"
34 : :
35 : : #include <QProgressDialog>
36 : :
37 : : typedef QgsVectorLayerExporter::ExportError createEmptyLayer_t(
38 : : const QString &uri,
39 : : const QgsFields &fields,
40 : : QgsWkbTypes::Type geometryType,
41 : : const QgsCoordinateReferenceSystem &destCRS,
42 : : bool overwrite,
43 : : QMap<int, int> *oldToNewAttrIdx,
44 : : QString *errorMessage,
45 : : const QMap<QString, QVariant> *options
46 : : );
47 : :
48 : :
49 : 0 : QgsVectorLayerExporter::QgsVectorLayerExporter( const QString &uri,
50 : : const QString &providerKey,
51 : : const QgsFields &fields,
52 : : QgsWkbTypes::Type geometryType,
53 : : const QgsCoordinateReferenceSystem &crs,
54 : : bool overwrite,
55 : : const QMap<QString, QVariant> &options,
56 : : QgsFeatureSink::SinkFlags sinkFlags )
57 : 0 : : mErrorCount( 0 )
58 : 0 : , mAttributeCount( -1 )
59 : :
60 : 0 : {
61 : 0 : mProvider = nullptr;
62 : :
63 : 0 : QMap<QString, QVariant> modifiedOptions( options );
64 : :
65 : 0 : if ( providerKey == QLatin1String( "ogr" ) &&
66 : 0 : options.contains( QStringLiteral( "driverName" ) ) &&
67 : 0 : ( options[ QStringLiteral( "driverName" ) ].toString().compare( QLatin1String( "GPKG" ), Qt::CaseInsensitive ) == 0 ||
68 : 0 : options[ QStringLiteral( "driverName" ) ].toString().compare( QLatin1String( "SQLite" ), Qt::CaseInsensitive ) == 0 ) )
69 : : {
70 : 0 : if ( geometryType != QgsWkbTypes::NoGeometry )
71 : : {
72 : : // For GPKG/Spatialite, we explicitly ask not to create a spatial index at
73 : : // layer creation since this would slow down inserts. Defer its creation
74 : : // to end of exportLayer() or destruction of this object.
75 : 0 : QStringList modifiedLayerOptions;
76 : 0 : if ( options.contains( QStringLiteral( "layerOptions" ) ) )
77 : : {
78 : 0 : QStringList layerOptions = options.value( QStringLiteral( "layerOptions" ) ).toStringList();
79 : 0 : for ( const QString &layerOption : layerOptions )
80 : : {
81 : 0 : if ( layerOption.compare( QLatin1String( "SPATIAL_INDEX=YES" ), Qt::CaseInsensitive ) == 0 ||
82 : 0 : layerOption.compare( QLatin1String( "SPATIAL_INDEX=ON" ), Qt::CaseInsensitive ) == 0 ||
83 : 0 : layerOption.compare( QLatin1String( "SPATIAL_INDEX=TRUE" ), Qt::CaseInsensitive ) == 0 ||
84 : 0 : layerOption.compare( QLatin1String( "SPATIAL_INDEX=1" ), Qt::CaseInsensitive ) == 0 )
85 : : {
86 : : // do nothing
87 : 0 : }
88 : 0 : else if ( layerOption.compare( QLatin1String( "SPATIAL_INDEX=NO" ), Qt::CaseInsensitive ) == 0 ||
89 : 0 : layerOption.compare( QLatin1String( "SPATIAL_INDEX=OFF" ), Qt::CaseInsensitive ) == 0 ||
90 : 0 : layerOption.compare( QLatin1String( "SPATIAL_INDEX=FALSE" ), Qt::CaseInsensitive ) == 0 ||
91 : 0 : layerOption.compare( QLatin1String( "SPATIAL_INDEX=0" ), Qt::CaseInsensitive ) == 0 )
92 : : {
93 : 0 : mCreateSpatialIndex = false;
94 : 0 : }
95 : : else
96 : : {
97 : 0 : modifiedLayerOptions << layerOption;
98 : : }
99 : : }
100 : 0 : }
101 : 0 : modifiedLayerOptions << QStringLiteral( "SPATIAL_INDEX=FALSE" );
102 : 0 : modifiedOptions[ QStringLiteral( "layerOptions" ) ] = modifiedLayerOptions;
103 : 0 : }
104 : 0 : }
105 : :
106 : : // create an empty layer
107 : 0 : QString errMsg;
108 : 0 : QgsProviderRegistry *pReg = QgsProviderRegistry::instance();
109 : 0 : mError = pReg->createEmptyLayer( providerKey, uri, fields, geometryType, crs, overwrite, mOldToNewAttrIdx,
110 : 0 : errMsg, !modifiedOptions.isEmpty() ? &modifiedOptions : nullptr );
111 : 0 : if ( errorCode() )
112 : : {
113 : 0 : mErrorMessage = errMsg;
114 : 0 : return;
115 : : }
116 : :
117 : 0 : const auto constMOldToNewAttrIdx = mOldToNewAttrIdx;
118 : 0 : for ( int idx : constMOldToNewAttrIdx )
119 : : {
120 : 0 : if ( idx > mAttributeCount )
121 : 0 : mAttributeCount = idx;
122 : : }
123 : :
124 : 0 : mAttributeCount++;
125 : :
126 : 0 : QgsDebugMsgLevel( QStringLiteral( "Created empty layer" ), 2 );
127 : :
128 : 0 : QString uriUpdated( uri );
129 : : // HACK sorry...
130 : 0 : if ( providerKey == QLatin1String( "ogr" ) )
131 : : {
132 : 0 : QString layerName;
133 : 0 : if ( options.contains( QStringLiteral( "layerName" ) ) )
134 : 0 : layerName = options.value( QStringLiteral( "layerName" ) ).toString();
135 : 0 : if ( !layerName.isEmpty() )
136 : : {
137 : 0 : uriUpdated += QLatin1String( "|layername=" );
138 : 0 : uriUpdated += layerName;
139 : 0 : }
140 : 0 : }
141 : :
142 : : // Oracle specific HACK: we cannot guess the geometry type when there is no rows, so we need
143 : : // to force it in the uri
144 : 0 : if ( providerKey == QLatin1String( "oracle" ) )
145 : : {
146 : 0 : uriUpdated += QStringLiteral( " type=%1" ).arg( QgsWkbTypes::displayString( geometryType ) );
147 : 0 : }
148 : :
149 : 0 : QgsDataProvider::ProviderOptions providerOptions;
150 : 0 : QgsVectorDataProvider *vectorProvider = qobject_cast< QgsVectorDataProvider * >( pReg->createProvider( providerKey, uriUpdated, providerOptions ) );
151 : 0 : if ( !vectorProvider || !vectorProvider->isValid() || ( vectorProvider->capabilities() & QgsVectorDataProvider::AddFeatures ) == 0 )
152 : : {
153 : 0 : mError = ErrInvalidLayer;
154 : 0 : mErrorMessage = QObject::tr( "Loading of layer failed" );
155 : :
156 : 0 : delete vectorProvider;
157 : 0 : return;
158 : : }
159 : :
160 : : // If the result is a geopackage layer and there is already a field name FID requested which
161 : 0 : // might contain duplicates, make sure to generate a new field with a unique name instead
162 : : // that will be filled by ogr with unique values.
163 : :
164 : : // HACK sorry
165 : 0 : const QString path = QgsProviderRegistry::instance()->decodeUri( QStringLiteral( "ogr" ), uri ).value( QStringLiteral( "path" ) ).toString();
166 : 0 : if ( sinkFlags.testFlag( QgsFeatureSink::SinkFlag::RegeneratePrimaryKey ) && path.endsWith( QLatin1String( ".gpkg" ), Qt::CaseInsensitive ) )
167 : : {
168 : 0 : QString fidName = options.value( QStringLiteral( "FID" ), QStringLiteral( "FID" ) ).toString();
169 : 0 : int fidIdx = fields.lookupField( fidName );
170 : 0 : if ( fidIdx != -1 )
171 : : {
172 : 0 : mOldToNewAttrIdx.remove( fidIdx );
173 : 0 : }
174 : 0 : }
175 : :
176 : 0 : mProvider = vectorProvider;
177 : 0 : mError = NoError;
178 : 0 : }
179 : :
180 : 0 : QgsVectorLayerExporter::~QgsVectorLayerExporter()
181 : 0 : {
182 : 0 : flushBuffer();
183 : :
184 : 0 : if ( mCreateSpatialIndex )
185 : : {
186 : 0 : createSpatialIndex();
187 : 0 : }
188 : :
189 : 0 : delete mProvider;
190 : 0 : }
191 : :
192 : 0 : QgsVectorLayerExporter::ExportError QgsVectorLayerExporter::errorCode() const
193 : : {
194 : 0 : return mError;
195 : : }
196 : :
197 : 0 : QString QgsVectorLayerExporter::errorMessage() const
198 : : {
199 : 0 : return mErrorMessage;
200 : : }
201 : :
202 : 0 : bool QgsVectorLayerExporter::addFeatures( QgsFeatureList &features, Flags flags )
203 : : {
204 : 0 : QgsFeatureList::iterator fIt = features.begin();
205 : 0 : bool result = true;
206 : 0 : for ( ; fIt != features.end(); ++fIt )
207 : : {
208 : 0 : result = result && addFeature( *fIt, flags );
209 : 0 : }
210 : 0 : return result;
211 : : }
212 : :
213 : 0 : bool QgsVectorLayerExporter::addFeature( QgsFeature &feat, Flags )
214 : : {
215 : 0 : QgsAttributes attrs = feat.attributes();
216 : :
217 : 0 : QgsFeature newFeat;
218 : 0 : if ( feat.hasGeometry() )
219 : 0 : newFeat.setGeometry( feat.geometry() );
220 : :
221 : 0 : newFeat.initAttributes( mAttributeCount );
222 : :
223 : 0 : for ( int i = 0; i < attrs.count(); ++i )
224 : : {
225 : : // add only mapped attributes (un-mapped ones will not be present in the
226 : : // destination layer)
227 : 0 : int dstIdx = mOldToNewAttrIdx.value( i, -1 );
228 : 0 : if ( dstIdx < 0 )
229 : 0 : continue;
230 : :
231 : 0 : QgsDebugMsgLevel( QStringLiteral( "moving field from pos %1 to %2" ).arg( i ).arg( dstIdx ), 3 );
232 : 0 : newFeat.setAttribute( dstIdx, attrs.at( i ) );
233 : 0 : }
234 : :
235 : 0 : mFeatureBuffer.append( newFeat );
236 : 0 : mFeatureBufferMemoryUsage += newFeat.approximateMemoryUsage();
237 : :
238 : 0 : if ( mFeatureBufferMemoryUsage >= 100 * 1000 * 1000 )
239 : : {
240 : 0 : return flushBuffer();
241 : : }
242 : :
243 : 0 : return true;
244 : 0 : }
245 : :
246 : 0 : QString QgsVectorLayerExporter::lastError() const
247 : : {
248 : 0 : return mErrorMessage;
249 : : }
250 : :
251 : 0 : bool QgsVectorLayerExporter::flushBuffer()
252 : 0 : {
253 : 0 : mFeatureBufferMemoryUsage = 0;
254 : 0 : if ( mFeatureBuffer.count() <= 0 )
255 : 0 : return true;
256 : :
257 : 0 : if ( !mProvider->addFeatures( mFeatureBuffer, QgsFeatureSink::FastInsert ) )
258 : : {
259 : 0 : QStringList errors = mProvider->errors();
260 : 0 : mProvider->clearErrors();
261 : :
262 : 0 : mErrorMessage = QObject::tr( "Creation error for features from #%1 to #%2. Provider errors was: \n%3" )
263 : 0 : .arg( mFeatureBuffer.first().id() )
264 : 0 : .arg( mFeatureBuffer.last().id() )
265 : 0 : .arg( errors.join( QLatin1Char( '\n' ) ) );
266 : :
267 : 0 : mError = ErrFeatureWriteFailed;
268 : 0 : mErrorCount += mFeatureBuffer.count();
269 : :
270 : 0 : mFeatureBuffer.clear();
271 : 0 : QgsDebugMsg( mErrorMessage );
272 : 0 : return false;
273 : 0 : }
274 : :
275 : 0 : mFeatureBuffer.clear();
276 : 0 : return true;
277 : 0 : }
278 : :
279 : 0 : bool QgsVectorLayerExporter::createSpatialIndex()
280 : : {
281 : 0 : mCreateSpatialIndex = false;
282 : 0 : if ( mProvider && ( mProvider->capabilities() & QgsVectorDataProvider::CreateSpatialIndex ) != 0 )
283 : : {
284 : 0 : return mProvider->createSpatialIndex();
285 : : }
286 : : else
287 : : {
288 : 0 : return true;
289 : : }
290 : 0 : }
291 : :
292 : : QgsVectorLayerExporter::ExportError
293 : 0 : QgsVectorLayerExporter::exportLayer( QgsVectorLayer *layer,
294 : : const QString &uri,
295 : : const QString &providerKey,
296 : : const QgsCoordinateReferenceSystem &destCRS,
297 : : bool onlySelected,
298 : : QString *errorMessage,
299 : : const QMap<QString, QVariant> &options,
300 : : QgsFeedback *feedback )
301 : : {
302 : 0 : QgsCoordinateReferenceSystem outputCRS;
303 : 0 : QgsCoordinateTransform ct;
304 : 0 : bool shallTransform = false;
305 : :
306 : 0 : if ( !layer )
307 : 0 : return ErrInvalidLayer;
308 : :
309 : 0 : if ( destCRS.isValid() )
310 : : {
311 : : // This means we should transform
312 : 0 : outputCRS = destCRS;
313 : 0 : shallTransform = true;
314 : 0 : }
315 : : else
316 : : {
317 : : // This means we shouldn't transform, use source CRS as output (if defined)
318 : 0 : outputCRS = layer->crs();
319 : : }
320 : :
321 : :
322 : 0 : bool overwrite = false;
323 : 0 : bool forceSinglePartGeom = false;
324 : 0 : QMap<QString, QVariant> providerOptions = options;
325 : 0 : if ( !options.isEmpty() )
326 : : {
327 : 0 : overwrite = providerOptions.take( QStringLiteral( "overwrite" ) ).toBool();
328 : 0 : forceSinglePartGeom = providerOptions.take( QStringLiteral( "forceSinglePartGeometryType" ) ).toBool();
329 : 0 : }
330 : :
331 : 0 : QgsFields fields = layer->fields();
332 : :
333 : 0 : QgsWkbTypes::Type wkbType = layer->wkbType();
334 : :
335 : : // Special handling for Shapefiles
336 : 0 : if ( layer->providerType() == QLatin1String( "ogr" ) && layer->storageType() == QLatin1String( "ESRI Shapefile" ) )
337 : : {
338 : : // convert field names to lowercase
339 : 0 : for ( int fldIdx = 0; fldIdx < fields.count(); ++fldIdx )
340 : : {
341 : 0 : fields.rename( fldIdx, fields.at( fldIdx ).name().toLower() );
342 : 0 : }
343 : 0 : }
344 : :
345 : 0 : bool convertGeometryToSinglePart = false;
346 : 0 : if ( forceSinglePartGeom && QgsWkbTypes::isMultiType( wkbType ) )
347 : : {
348 : 0 : wkbType = QgsWkbTypes::singleType( wkbType );
349 : 0 : convertGeometryToSinglePart = true;
350 : 0 : }
351 : :
352 : 0 : QgsVectorLayerExporter *writer =
353 : 0 : new QgsVectorLayerExporter( uri, providerKey, fields, wkbType, outputCRS, overwrite, providerOptions );
354 : :
355 : : // check whether file creation was successful
356 : 0 : ExportError err = writer->errorCode();
357 : 0 : if ( err != NoError )
358 : : {
359 : 0 : if ( errorMessage )
360 : 0 : *errorMessage = writer->errorMessage();
361 : 0 : delete writer;
362 : 0 : return err;
363 : : }
364 : :
365 : 0 : if ( errorMessage )
366 : : {
367 : 0 : errorMessage->clear();
368 : 0 : }
369 : :
370 : 0 : QgsFeature fet;
371 : :
372 : 0 : QgsFeatureRequest req;
373 : 0 : if ( wkbType == QgsWkbTypes::NoGeometry )
374 : 0 : req.setFlags( QgsFeatureRequest::NoGeometry );
375 : 0 : if ( onlySelected )
376 : 0 : req.setFilterFids( layer->selectedFeatureIds() );
377 : :
378 : 0 : QgsFeatureIterator fit = layer->getFeatures( req );
379 : :
380 : : // Create our transform
381 : 0 : if ( destCRS.isValid() )
382 : : {
383 : 0 : ct = QgsCoordinateTransform( layer->crs(), destCRS, layer->transformContext() );
384 : 0 : }
385 : :
386 : : // Check for failure
387 : 0 : if ( !ct.isValid() )
388 : 0 : shallTransform = false;
389 : :
390 : 0 : long n = 0;
391 : 0 : long approxTotal = onlySelected ? layer->selectedFeatureCount() : layer->featureCount();
392 : :
393 : 0 : if ( errorMessage )
394 : : {
395 : 0 : *errorMessage = QObject::tr( "Feature write errors:" );
396 : 0 : }
397 : :
398 : 0 : bool canceled = false;
399 : :
400 : : // write all features
401 : 0 : while ( fit.nextFeature( fet ) )
402 : : {
403 : 0 : if ( feedback && feedback->isCanceled() )
404 : : {
405 : 0 : canceled = true;
406 : 0 : if ( errorMessage )
407 : : {
408 : 0 : *errorMessage += '\n' + QObject::tr( "Import was canceled at %1 of %2" ).arg( n ).arg( approxTotal );
409 : 0 : }
410 : 0 : break;
411 : : }
412 : :
413 : 0 : if ( writer->errorCount() > 1000 )
414 : : {
415 : 0 : if ( errorMessage )
416 : : {
417 : 0 : *errorMessage += '\n' + QObject::tr( "Stopping after %1 errors" ).arg( writer->errorCount() );
418 : 0 : }
419 : 0 : break;
420 : : }
421 : :
422 : 0 : if ( shallTransform )
423 : : {
424 : : try
425 : : {
426 : 0 : if ( fet.hasGeometry() )
427 : : {
428 : 0 : QgsGeometry g = fet.geometry();
429 : 0 : g.transform( ct );
430 : 0 : fet.setGeometry( g );
431 : 0 : }
432 : 0 : }
433 : : catch ( QgsCsException &e )
434 : : {
435 : 0 : delete writer;
436 : :
437 : 0 : QString msg = QObject::tr( "Failed to transform a point while drawing a feature with ID '%1'. Writing stopped. (Exception: %2)" )
438 : 0 : .arg( fet.id() ).arg( e.what() );
439 : 0 : QgsMessageLog::logMessage( msg, QObject::tr( "Vector import" ) );
440 : 0 : if ( errorMessage )
441 : 0 : *errorMessage += '\n' + msg;
442 : :
443 : 0 : return ErrProjection;
444 : 0 : }
445 : 0 : }
446 : :
447 : : // Handles conversion to single-part
448 : 0 : if ( convertGeometryToSinglePart && fet.geometry().isMultipart() )
449 : : {
450 : 0 : QgsGeometry singlePartGeometry { fet.geometry() };
451 : : // We want a failure if the geometry cannot be converted to single-part without data loss!
452 : : // check if there are more than one part
453 : 0 : const QgsGeometryCollection *c = qgsgeometry_cast<const QgsGeometryCollection *>( singlePartGeometry.constGet() );
454 : 0 : if ( ( c && c->partCount() > 1 ) || ! singlePartGeometry.convertToSingleType() )
455 : : {
456 : 0 : delete writer;
457 : 0 : QString msg = QObject::tr( "Failed to transform a feature with ID '%1' to single part. Writing stopped." )
458 : 0 : .arg( fet.id() );
459 : 0 : QgsMessageLog::logMessage( msg, QObject::tr( "Vector import" ) );
460 : 0 : if ( errorMessage )
461 : 0 : *errorMessage += '\n' + msg;
462 : 0 : return ErrFeatureWriteFailed;
463 : 0 : }
464 : 0 : fet.setGeometry( singlePartGeometry );
465 : 0 : }
466 : :
467 : 0 : if ( !writer->addFeature( fet ) )
468 : : {
469 : 0 : if ( writer->errorCode() && errorMessage )
470 : : {
471 : 0 : *errorMessage += '\n' + writer->errorMessage();
472 : 0 : }
473 : 0 : }
474 : 0 : n++;
475 : :
476 : 0 : if ( feedback )
477 : : {
478 : 0 : feedback->setProgress( 100.0 * static_cast< double >( n ) / approxTotal );
479 : 0 : }
480 : :
481 : : }
482 : :
483 : : // flush the buffer to be sure that all features are written
484 : 0 : if ( !writer->flushBuffer() )
485 : : {
486 : 0 : if ( writer->errorCode() && errorMessage )
487 : : {
488 : 0 : *errorMessage += '\n' + writer->errorMessage();
489 : 0 : }
490 : 0 : }
491 : 0 : int errors = writer->errorCount();
492 : :
493 : 0 : if ( writer->mCreateSpatialIndex && !writer->createSpatialIndex() )
494 : : {
495 : 0 : if ( writer->errorCode() && errorMessage )
496 : : {
497 : 0 : *errorMessage += '\n' + writer->errorMessage();
498 : 0 : }
499 : 0 : }
500 : :
501 : 0 : delete writer;
502 : :
503 : 0 : if ( errorMessage )
504 : : {
505 : 0 : if ( errors > 0 )
506 : : {
507 : 0 : *errorMessage += '\n' + QObject::tr( "Only %1 of %2 features written." ).arg( n - errors ).arg( n );
508 : 0 : }
509 : : else
510 : : {
511 : 0 : errorMessage->clear();
512 : : }
513 : 0 : }
514 : :
515 : 0 : if ( canceled )
516 : 0 : return ErrUserCanceled;
517 : 0 : else if ( errors > 0 )
518 : 0 : return ErrFeatureWriteFailed;
519 : :
520 : 0 : return NoError;
521 : 0 : }
522 : :
523 : :
524 : : //
525 : : // QgsVectorLayerExporterTask
526 : : //
527 : :
528 : 0 : QgsVectorLayerExporterTask::QgsVectorLayerExporterTask( QgsVectorLayer *layer, const QString &uri, const QString &providerKey, const QgsCoordinateReferenceSystem &destinationCrs, const QMap<QString, QVariant> &options, bool ownsLayer )
529 : 0 : : QgsTask( tr( "Exporting %1" ).arg( layer->name() ), QgsTask::CanCancel )
530 : 0 : , mLayer( layer )
531 : 0 : , mOwnsLayer( ownsLayer )
532 : 0 : , mDestUri( uri )
533 : 0 : , mDestProviderKey( providerKey )
534 : 0 : , mDestCrs( destinationCrs )
535 : 0 : , mOptions( options )
536 : 0 : , mOwnedFeedback( new QgsFeedback() )
537 : 0 : {
538 : 0 : if ( mLayer )
539 : 0 : setDependentLayers( QList< QgsMapLayer * >() << mLayer );
540 : 0 : }
541 : :
542 : 0 : QgsVectorLayerExporterTask *QgsVectorLayerExporterTask::withLayerOwnership( QgsVectorLayer *layer, const QString &uri, const QString &providerKey, const QgsCoordinateReferenceSystem &destinationCrs, const QMap<QString, QVariant> &options )
543 : : {
544 : 0 : std::unique_ptr< QgsVectorLayerExporterTask > newTask( new QgsVectorLayerExporterTask( layer, uri, providerKey, destinationCrs, options ) );
545 : 0 : newTask->mOwnsLayer = true;
546 : 0 : return newTask.release();
547 : 0 : }
548 : :
549 : 0 : void QgsVectorLayerExporterTask::cancel()
550 : : {
551 : 0 : mOwnedFeedback->cancel();
552 : 0 : QgsTask::cancel();
553 : 0 : }
554 : :
555 : 0 : bool QgsVectorLayerExporterTask::run()
556 : : {
557 : 0 : if ( !mLayer )
558 : 0 : return false;
559 : :
560 : 0 : connect( mOwnedFeedback.get(), &QgsFeedback::progressChanged, this, &QgsVectorLayerExporterTask::setProgress );
561 : :
562 : :
563 : 0 : mError = QgsVectorLayerExporter::exportLayer(
564 : 0 : mLayer.data(), mDestUri, mDestProviderKey, mDestCrs, false, &mErrorMessage,
565 : 0 : mOptions, mOwnedFeedback.get() );
566 : :
567 : 0 : return mError == QgsVectorLayerExporter::NoError;
568 : 0 : }
569 : :
570 : 0 : void QgsVectorLayerExporterTask::finished( bool result )
571 : : {
572 : : // QgsMapLayer has QTimer member, which must not be destroyed from another thread
573 : 0 : if ( mOwnsLayer )
574 : 0 : delete mLayer;
575 : :
576 : 0 : if ( result )
577 : 0 : emit exportComplete();
578 : : else
579 : 0 : emit errorOccurred( mError, mErrorMessage );
580 : 0 : }
|