QGIS API Documentation  2.99.0-Master (314842d)
qgsvectorlayerimport.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsvectorlayerimport.cpp
3  vector layer importer
4  -------------------
5  begin : Thu Aug 25 2011
6  copyright : (C) 2011 by Giuseppe Sucameli
7  email : brush.tyler at gmail.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 "qgsfields.h"
20 #include "qgsfeature.h"
21 #include "qgsfeatureiterator.h"
22 #include "qgsgeometry.h"
23 #include "qgslogger.h"
24 #include "qgsmessagelog.h"
26 #include "qgsvectorlayerimport.h"
27 #include "qgsproviderregistry.h"
28 #include "qgsdatasourceuri.h"
29 #include "qgscsexception.h"
30 #include "qgsvectordataprovider.h"
31 #include "qgsvectorlayer.h"
32 
33 #include <QProgressDialog>
34 
35 #define FEATURE_BUFFER_SIZE 200
36 
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 
50  const QString &providerKey,
51  const QgsFields &fields,
52  QgsWkbTypes::Type geometryType,
54  bool overwrite,
55  const QMap<QString, QVariant> *options,
56  QProgressDialog *progress )
57  : mErrorCount( 0 )
58  , mAttributeCount( -1 )
59  , mProgress( progress )
60 
61 {
62  mProvider = nullptr;
63 
65 
66  std::unique_ptr< QLibrary > myLib( pReg->providerLibrary( providerKey ) );
67  if ( !myLib )
68  {
70  mErrorMessage = QObject::tr( "Unable to load %1 provider" ).arg( providerKey );
71  return;
72  }
73 
74  createEmptyLayer_t *pCreateEmpty = reinterpret_cast< createEmptyLayer_t * >( cast_to_fptr( myLib->resolve( "createEmptyLayer" ) ) );
75  if ( !pCreateEmpty )
76  {
78  mErrorMessage = QObject::tr( "Provider %1 has no %2 method" ).arg( providerKey, QStringLiteral( "createEmptyLayer" ) );
79  return;
80  }
81 
82  // create an empty layer
83  QString errMsg;
84  mError = pCreateEmpty( uri, fields, geometryType, crs, overwrite, &mOldToNewAttrIdx, &errMsg, options );
85  if ( hasError() )
86  {
87  mErrorMessage = errMsg;
88  return;
89  }
90 
91  Q_FOREACH ( int idx, mOldToNewAttrIdx )
92  {
93  if ( idx > mAttributeCount )
94  mAttributeCount = idx;
95  }
96 
98 
99  QgsDebugMsg( "Created empty layer" );
100 
101  QgsVectorDataProvider *vectorProvider = dynamic_cast< QgsVectorDataProvider * >( pReg->provider( providerKey, uri ) );
102  if ( !vectorProvider || !vectorProvider->isValid() || ( vectorProvider->capabilities() & QgsVectorDataProvider::AddFeatures ) == 0 )
103  {
105  mErrorMessage = QObject::tr( "Loading of layer failed" );
106 
107  if ( vectorProvider )
108  delete vectorProvider;
109 
110  return;
111  }
112 
113  mProvider = vectorProvider;
114  mError = NoError;
115 }
116 
118 {
119  flushBuffer();
120 
121  if ( mProvider )
122  delete mProvider;
123 }
124 
126 {
127  return mError;
128 }
129 
131 {
132  return mErrorMessage;
133 }
134 
136 {
137  QgsAttributes attrs = feat.attributes();
138 
139  QgsFeature newFeat;
140  if ( feat.hasGeometry() )
141  newFeat.setGeometry( feat.geometry() );
142 
143  newFeat.initAttributes( mAttributeCount );
144 
145  for ( int i = 0; i < attrs.count(); ++i )
146  {
147  // add only mapped attributes (un-mapped ones will not be present in the
148  // destination layer)
149  int dstIdx = mOldToNewAttrIdx.value( i, -1 );
150  if ( dstIdx < 0 )
151  continue;
152 
153  QgsDebugMsgLevel( QString( "moving field from pos %1 to %2" ).arg( i ).arg( dstIdx ), 3 );
154  newFeat.setAttribute( dstIdx, attrs.at( i ) );
155  }
156 
157  mFeatureBuffer.append( newFeat );
158 
159  if ( mFeatureBuffer.count() >= FEATURE_BUFFER_SIZE )
160  {
161  return flushBuffer();
162  }
163 
164  return true;
165 }
166 
168 {
169  if ( mFeatureBuffer.count() <= 0 )
170  return true;
171 
173  {
174  QStringList errors = mProvider->errors();
176 
177  mErrorMessage = QObject::tr( "Creation error for features from #%1 to #%2. Provider errors was: \n%3" )
178  .arg( mFeatureBuffer.first().id() )
179  .arg( mFeatureBuffer.last().id() )
180  .arg( errors.join( QStringLiteral( "\n" ) ) );
181 
183  mErrorCount += mFeatureBuffer.count();
184 
185  mFeatureBuffer.clear();
187  return false;
188  }
189 
190  mFeatureBuffer.clear();
191  return true;
192 }
193 
195 {
197  {
198  return mProvider->createSpatialIndex();
199  }
200  else
201  {
202  return true;
203  }
204 }
205 
208  const QString &uri,
209  const QString &providerKey,
210  const QgsCoordinateReferenceSystem &destCRS,
211  bool onlySelected,
212  QString *errorMessage,
213  bool skipAttributeCreation,
214  QMap<QString, QVariant> *options,
215  QProgressDialog *progress )
216 {
219  bool shallTransform = false;
220 
221  if ( !layer )
222  return ErrInvalidLayer;
223 
224  if ( destCRS.isValid() )
225  {
226  // This means we should transform
227  outputCRS = destCRS;
228  shallTransform = true;
229  }
230  else
231  {
232  // This means we shouldn't transform, use source CRS as output (if defined)
233  outputCRS = layer->crs();
234  }
235 
236 
237  bool overwrite = false;
238  bool forceSinglePartGeom = false;
239  if ( options )
240  {
241  overwrite = options->take( QStringLiteral( "overwrite" ) ).toBool();
242  forceSinglePartGeom = options->take( QStringLiteral( "forceSinglePartGeometryType" ) ).toBool();
243  }
244 
245  QgsFields fields = skipAttributeCreation ? QgsFields() : layer->fields();
246  QgsWkbTypes::Type wkbType = layer->wkbType();
247 
248  // Special handling for Shapefiles
249  if ( layer->providerType() == QLatin1String( "ogr" ) && layer->storageType() == QLatin1String( "ESRI Shapefile" ) )
250  {
251  // convert field names to lowercase
252  for ( int fldIdx = 0; fldIdx < fields.count(); ++fldIdx )
253  {
254  fields[fldIdx].setName( fields.at( fldIdx ).name().toLower() );
255  }
256 
257  if ( !forceSinglePartGeom )
258  {
259  // convert wkbtype to multipart (see #5547)
260  switch ( wkbType )
261  {
262  case QgsWkbTypes::Point:
263  wkbType = QgsWkbTypes::MultiPoint;
264  break;
267  break;
269  wkbType = QgsWkbTypes::MultiPolygon;
270  break;
272  wkbType = QgsWkbTypes::MultiPoint25D;
273  break;
276  break;
279  break;
280  default:
281  break;
282  }
283  }
284  }
285 
286  QgsVectorLayerImport *writer =
287  new QgsVectorLayerImport( uri, providerKey, fields, wkbType, outputCRS, overwrite, options, progress );
288 
289  // check whether file creation was successful
290  ImportError err = writer->hasError();
291  if ( err != NoError )
292  {
293  if ( errorMessage )
294  *errorMessage = writer->errorMessage();
295  delete writer;
296  return err;
297  }
298 
299  if ( errorMessage )
300  {
301  errorMessage->clear();
302  }
303 
304  QgsAttributeList allAttr = skipAttributeCreation ? QgsAttributeList() : layer->attributeList();
305  QgsFeature fet;
306 
307  QgsFeatureRequest req;
308  if ( wkbType == QgsWkbTypes::NoGeometry )
310  if ( skipAttributeCreation )
312 
313  QgsFeatureIterator fit = layer->getFeatures( req );
314 
315  const QgsFeatureIds &ids = layer->selectedFeatureIds();
316 
317  // Create our transform
318  if ( destCRS.isValid() )
319  ct = QgsCoordinateTransform( layer->crs(), destCRS );
320 
321  // Check for failure
322  if ( !ct.isValid() )
323  shallTransform = false;
324 
325  int n = 0;
326 
327  if ( errorMessage )
328  {
329  *errorMessage = QObject::tr( "Feature write errors:" );
330  }
331 
332  if ( progress )
333  {
334  progress->setRange( 0, layer->featureCount() );
335  }
336 
337  bool canceled = false;
338 
339  // write all features
340  while ( fit.nextFeature( fet ) )
341  {
342  if ( progress && progress->wasCanceled() )
343  {
344  canceled = true;
345  if ( errorMessage )
346  {
347  *errorMessage += '\n' + QObject::tr( "Import was canceled at %1 of %2" ).arg( progress->value() ).arg( progress->maximum() );
348  }
349  break;
350  }
351 
352  if ( writer->errorCount() > 1000 )
353  {
354  if ( errorMessage )
355  {
356  *errorMessage += '\n' + QObject::tr( "Stopping after %1 errors" ).arg( writer->errorCount() );
357  }
358  break;
359  }
360 
361  if ( onlySelected && !ids.contains( fet.id() ) )
362  continue;
363 
364  if ( shallTransform )
365  {
366  try
367  {
368  if ( fet.hasGeometry() )
369  {
370  QgsGeometry g = fet.geometry();
371  g.transform( ct );
372  fet.setGeometry( g );
373  }
374  }
375  catch ( QgsCsException &e )
376  {
377  delete writer;
378 
379  QString msg = QObject::tr( "Failed to transform a point while drawing a feature with ID '%1'. Writing stopped. (Exception: %2)" )
380  .arg( fet.id() ).arg( e.what() );
381  QgsMessageLog::logMessage( msg, QObject::tr( "Vector import" ) );
382  if ( errorMessage )
383  *errorMessage += '\n' + msg;
384 
385  return ErrProjection;
386  }
387  }
388  if ( skipAttributeCreation )
389  {
390  fet.initAttributes( 0 );
391  }
392  if ( !writer->addFeature( fet ) )
393  {
394  if ( writer->hasError() && errorMessage )
395  {
396  *errorMessage += '\n' + writer->errorMessage();
397  }
398  }
399  n++;
400 
401  if ( progress )
402  {
403  progress->setValue( n );
404  }
405  }
406 
407  // flush the buffer to be sure that all features are written
408  if ( !writer->flushBuffer() )
409  {
410  if ( writer->hasError() && errorMessage )
411  {
412  *errorMessage += '\n' + writer->errorMessage();
413  }
414  }
415  int errors = writer->errorCount();
416 
417  if ( !writer->createSpatialIndex() )
418  {
419  if ( writer->hasError() && errorMessage )
420  {
421  *errorMessage += '\n' + writer->errorMessage();
422  }
423  }
424 
425  delete writer;
426 
427  if ( errorMessage )
428  {
429  if ( errors > 0 )
430  {
431  *errorMessage += '\n' + QObject::tr( "Only %1 of %2 features written." ).arg( n - errors ).arg( n );
432  }
433  else
434  {
435  errorMessage->clear();
436  }
437  }
438 
439  if ( canceled )
440  return ErrUserCanceled;
441  else if ( errors > 0 )
442  return ErrFeatureWriteFailed;
443 
444  return NoError;
445 }
QgsFeatureId id
Definition: qgsfeature.h:140
Wrapper for iterator of features from vector data provider or vector layer.
static QgsProviderRegistry * instance(const QString &pluginPath=QString::null)
Means of accessing canonical single instance.
virtual Capabilities capabilities() const
Returns flags containing the supported capabilities.
QString name
Definition: qgsfield.h:53
QMap< int, int > mOldToNewAttrIdx
Map attribute indexes to new field indexes.
QString storageType() const
Returns the permanent storage type for this layer as a friendly name.
#define QgsDebugMsg(str)
Definition: qgslogger.h:36
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeature.h:358
#define FEATURE_BUFFER_SIZE
QLibrary * providerLibrary(const QString &providerKey) const
Returns a new QLibrary for the specified providerKey.
static ImportError importLayer(QgsVectorLayer *layer, const QString &uri, const QString &providerKey, const QgsCoordinateReferenceSystem &destCRS, bool onlySelected=false, QString *errorMessage=nullptr, bool skipAttributeCreation=false, QMap< QString, QVariant > *options=nullptr, QProgressDialog *progress=nullptr)
Writes the contents of vector layer to a different datasource.
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
bool flushBuffer()
Flush the buffer writing the features to the new layer.
QgsVectorDataProvider * mProvider
Container of fields for a vector layer.
Definition: qgsfields.h:39
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:79
bool setAttribute(int field, const QVariant &attr)
Set an attribute&#39;s value by field index.
Definition: qgsfeature.cpp:216
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:136
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:202
virtual bool addFeatures(QgsFeatureList &flist)
Adds a list of features.
int count() const
Return number of items.
Definition: qgsfields.cpp:115
QString what() const
Definition: qgsexception.h:38
QgsFields fields() const
Returns the list of fields of this layer.
QgsField at(int i) const
Get field at particular index (must be in range 0..N-1)
Definition: qgsfields.cpp:135
Allows creation of spatial index.
virtual bool createSpatialIndex()
Creates a spatial index on the datasource (if supported by the provider type).
QgsWkbTypes::Type wkbType() const
Returns the WKBType or WKBUnknown in case of error.
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:65
const QgsFeatureIds & selectedFeatureIds() const
Return reference to identifiers of selected features.
A convenience class for writing vector files to disk.
QgsDataProvider * provider(const QString &providerKey, const QString &dataSource)
Create an instance of the provider.
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:37
virtual bool isValid() const =0
Returns true if this is a valid layer.
long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
void initAttributes(int fieldCount)
Initialize this feature with the given number of fields.
Definition: qgsfeature.cpp:207
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const
Query the layer for features specified in request.
QString errorMessage()
Retrieves error message.
static void logMessage(const QString &message, const QString &tag=QString::null, MessageLevel level=WARNING)
add a message to the instance (and create it if necessary)
QStringList errors() const
Get recorded errors.
This class wraps a request for features to a vector layer (or directly its vector data provider)...
QList< int > QgsAttributeList
QgsCoordinateReferenceSystem crs() const
Returns the layer&#39;s spatial reference system.
~QgsVectorLayerImport()
Close the new created layer.
void clearErrors()
Clear recorded errors.
QgsAttributeList attributeList() const
Returns list of attribute indexes.
QgsGeometry geometry() const
Returns the geometry associated with this feature.
Definition: qgsfeature.cpp:113
#define cast_to_fptr(f)
Definition: qgis.h:128
ImportError mError
Contains error value.
bool createSpatialIndex()
Create index.
A registry / canonical manager of data providers.
bool addFeature(QgsFeature &feature)
Add feature to the new created layer.
QgsVectorLayerImport::ImportError createEmptyLayer_t(const QString &uri, const QgsFields &fields, QgsWkbTypes::Type geometryType, const QgsCoordinateReferenceSystem &destCRS, bool overwrite, QMap< int, int > *oldToNewAttrIdx, QString *errorMessage, const QMap< QString, QVariant > *options)
QgsVectorLayerImport(const QString &uri, const QString &provider, const QgsFields &fields, QgsWkbTypes::Type geometryType, const QgsCoordinateReferenceSystem &crs, bool overwrite=false, const QMap< QString, QVariant > *options=nullptr, QProgressDialog *progress=nullptr)
Constructor for QgsVectorLayerImport.
ImportError hasError()
Checks whether there were any errors.
This class represents a coordinate reference system (CRS).
void setGeometry(const QgsGeometry &geometry)
Set the feature&#39;s geometry.
Definition: qgsfeature.cpp:149
Class for doing transforms between two map coordinate systems.
int transform(const QgsCoordinateTransform &ct)
Transform this geometry as described by CoordinateTransform ct.
Custom exception class for Coordinate Reference System related exceptions.
QString providerType() const
Return the provider type for this layer.
bool nextFeature(QgsFeature &f)
This is the base class for vector data providers.
Geometry is not required. It may still be returned if e.g. required for a filter condition.
A vector of attributes.
Definition: qgsfeature.h:56
Represents a vector layer which manages a vector based data sets.
QgsAttributes attributes
Definition: qgsfeature.h:141
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Set flags that affect how features will be fetched.
bool isValid() const
Returns whether this CRS is correctly initialized and usable.