QGIS API Documentation  2.99.0-Master (08c2e66)
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.
QgsAttributeList attributeList() const
Returns list of attribute indexes.
QString name
Definition: qgsfield.h:53
QMap< int, int > mOldToNewAttrIdx
Map attribute indexes to new field indexes.
#define QgsDebugMsg(str)
Definition: qgslogger.h:36
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeature.h:355
QgsFields fields() const
Returns the list of fields of this layer.
#define FEATURE_BUFFER_SIZE
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:213
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:136
QgsField at(int i) const
Get field at particular index (must be in range 0..N-1)
Definition: qgsfields.cpp:135
virtual bool addFeatures(QgsFeatureList &flist)
Adds a list of features.
Allows creation of spatial index.
virtual bool createSpatialIndex()
Creates a spatial index on the datasource (if supported by the provider type).
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const
Query the layer for features specified in request.
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:65
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.
void initAttributes(int fieldCount)
Initialize this feature with the given number of fields.
Definition: qgsfeature.cpp:204
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)
This class wraps a request for features to a vector layer (or directly its vector data provider)...
QList< int > QgsAttributeList
~QgsVectorLayerImport()
Close the new created layer.
long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
void clearErrors()
Clear recorded errors.
int count() const
Return number of items.
Definition: qgsfields.cpp:115
const QgsFeatureIds & selectedFeatureIds() const
Return reference to identifiers of selected features.
#define cast_to_fptr(f)
Definition: qgis.h:128
ImportError mError
Contains error value.
QgsGeometry geometry() const
Returns the geometry associated with this feature.
Definition: qgsfeature.cpp:113
QgsWkbTypes::Type wkbType() const
Returns the WKBType or WKBUnknown in case of error.
bool createSpatialIndex()
Create index.
A registry / canonical manager of data providers.
bool addFeature(QgsFeature &feature)
Add feature to the new created layer.
QLibrary * providerLibrary(const QString &providerKey) const
Returns a new QLibrary for the specified providerKey.
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
QString providerType() const
Return the provider type for this layer.
QString what() const
Definition: qgsexception.h:38
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)
virtual Capabilities capabilities() const
Returns flags containing the supported capabilities.
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:147
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:199
QStringList errors() const
Get recorded errors.
Class for doing transforms between two map coordinate systems.
int transform(const QgsCoordinateTransform &ct)
Transform this geometry as described by CoordinateTransform ct.
QgsCoordinateReferenceSystem crs() const
Returns the layer&#39;s spatial reference system.
Custom exception class for Coordinate Reference System related exceptions.
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.
QString storageType() const
Returns the permanent storage type for this layer as a friendly name.