QGIS API Documentation  2.5.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
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 "qgsfield.h"
20 #include "qgsfeature.h"
21 #include "qgsgeometry.h"
22 #include "qgslogger.h"
23 #include "qgsmessagelog.h"
25 #include "qgsvectorlayerimport.h"
26 #include "qgsproviderregistry.h"
27 #include "qgsdatasourceuri.h"
28 
29 #include <QProgressDialog>
30 
31 #define FEATURE_BUFFER_SIZE 200
32 
34  const QString &uri,
35  const QgsFields &fields,
36  QGis::WkbType geometryType,
37  const QgsCoordinateReferenceSystem *destCRS,
38  bool overwrite,
39  QMap<int, int> *oldToNewAttrIdx,
40  QString *errorMessage,
41  const QMap<QString, QVariant> *options
42 );
43 
44 
46  const QString &providerKey,
47  const QgsFields& fields,
48  QGis::WkbType geometryType,
50  bool overwrite,
51  const QMap<QString, QVariant> *options,
52  QProgressDialog *progress )
53  : mErrorCount( 0 )
54  , mProgress( progress )
55 {
56  mProvider = NULL;
57 
59 
60  QLibrary *myLib = pReg->providerLibrary( providerKey );
61  if ( !myLib )
62  {
64  mErrorMessage = QObject::tr( "Unable to load %1 provider" ).arg( providerKey );
65  return;
66  }
67 
68  createEmptyLayer_t * pCreateEmpty = ( createEmptyLayer_t * ) cast_to_fptr( myLib->resolve( "createEmptyLayer" ) );
69  if ( !pCreateEmpty )
70  {
71  delete myLib;
73  mErrorMessage = QObject::tr( "Provider %1 has no %2 method" ).arg( providerKey ).arg( "createEmptyLayer" );
74  return;
75  }
76 
77  delete myLib;
78 
79  // create an empty layer
80  QString errMsg;
81  mError = pCreateEmpty( uri, fields, geometryType, crs, overwrite, &mOldToNewAttrIdx, &errMsg, options );
82  if ( hasError() )
83  {
84  mErrorMessage = errMsg;
85  return;
86  }
87 
88  mAttributeCount = -1;
89 
90  foreach ( int idx, mOldToNewAttrIdx.values() )
91  {
92  if ( idx > mAttributeCount )
93  mAttributeCount = idx;
94  }
95 
97 
98  QgsDebugMsg( "Created empty layer" );
99 
100  QgsVectorDataProvider *vectorProvider = ( QgsVectorDataProvider* ) pReg->provider( providerKey, uri );
101  if ( !vectorProvider || !vectorProvider->isValid() || ( vectorProvider->capabilities() & QgsVectorDataProvider::AddFeatures ) == 0 )
102  {
104  mErrorMessage = QObject::tr( "Loading of layer failed" );
105 
106  if ( vectorProvider )
107  delete vectorProvider;
108 
109  return;
110  }
111 
112  mProvider = vectorProvider;
113  mError = NoError;
114 }
115 
117 {
118  flushBuffer();
119 
120  if ( mProvider )
121  delete mProvider;
122 }
123 
125 {
126  return mError;
127 }
128 
130 {
131  return mErrorMessage;
132 }
133 
135 {
136  const QgsAttributes &attrs = feat.attributes();
137 
138  QgsFeature newFeat;
139  if ( feat.geometry() )
140  newFeat.setGeometry( *feat.geometry() );
141 
142  newFeat.initAttributes( mAttributeCount );
143 
144  for ( int i = 0; i < attrs.count(); ++i )
145  {
146  // add only mapped attributes (un-mapped ones will not be present in the
147  // destination layer)
148  int dstIdx = mOldToNewAttrIdx.value( i, -1 );
149  if ( dstIdx < 0 )
150  continue;
151 
152  QgsDebugMsgLevel( QString( "moving field from pos %1 to %2" ).arg( i ).arg( dstIdx ), 3 );
153  newFeat.setAttribute( dstIdx, attrs[i] );
154  }
155 
156  mFeatureBuffer.append( newFeat );
157 
158  if ( mFeatureBuffer.count() >= FEATURE_BUFFER_SIZE )
159  {
160  return flushBuffer();
161  }
162 
163  return true;
164 }
165 
167 {
168  if ( mFeatureBuffer.count() <= 0 )
169  return true;
170 
172  {
173  QStringList errors = mProvider->errors();
175 
176  mErrorMessage = QObject::tr( "Creation error for features from #%1 to #%2. Provider errors was: \n%3" )
177  .arg( mFeatureBuffer.first().id() )
178  .arg( mFeatureBuffer.last().id() )
179  .arg( errors.join( "\n" ) );
180 
182  mErrorCount += mFeatureBuffer.count();
183 
184  mFeatureBuffer.clear();
186  return false;
187  }
188 
189  mFeatureBuffer.clear();
190  return true;
191 }
192 
194 {
196  {
197  return mProvider->createSpatialIndex();
198  }
199  else
200  {
201  return true;
202  }
203 }
204 
207  const QString& uri,
208  const QString& providerKey,
209  const QgsCoordinateReferenceSystem *destCRS,
210  bool onlySelected,
211  QString *errorMessage,
212  bool skipAttributeCreation,
213  QMap<QString, QVariant> *options,
214  QProgressDialog *progress )
215 {
216  const QgsCoordinateReferenceSystem* outputCRS;
217  QgsCoordinateTransform* ct = 0;
218  int shallTransform = false;
219 
220  if ( layer == NULL )
221  {
222  return ErrInvalidLayer;
223  }
224 
225  if ( destCRS && destCRS->isValid() )
226  {
227  // This means we should transform
228  outputCRS = destCRS;
229  shallTransform = true;
230  }
231  else
232  {
233  // This means we shouldn't transform, use source CRS as output (if defined)
234  outputCRS = &layer->crs();
235  }
236 
237 
238  bool overwrite = false;
239  bool forceSinglePartGeom = false;
240  if ( options )
241  {
242  overwrite = options->take( "overwrite" ).toBool();
243  forceSinglePartGeom = options->take( "forceSinglePartGeometryType" ).toBool();
244  }
245 
246  QgsFields fields = skipAttributeCreation ? QgsFields() : layer->pendingFields();
247  QGis::WkbType wkbType = layer->wkbType();
248 
249  // Special handling for Shapefiles
250  if ( layer->providerType() == "ogr" && layer->storageType() == "ESRI Shapefile" )
251  {
252  // convert field names to lowercase
253  for ( int fldIdx = 0; fldIdx < fields.count(); ++fldIdx )
254  {
255  fields[fldIdx].setName( fields[fldIdx].name().toLower() );
256  }
257 
258  if ( !forceSinglePartGeom )
259  {
260  // convert wkbtype to multipart (see #5547)
261  switch ( wkbType )
262  {
263  case QGis::WKBPoint:
264  wkbType = QGis::WKBMultiPoint;
265  break;
266  case QGis::WKBLineString:
267  wkbType = QGis::WKBMultiLineString;
268  break;
269  case QGis::WKBPolygon:
270  wkbType = QGis::WKBMultiPolygon;
271  break;
272  case QGis::WKBPoint25D:
273  wkbType = QGis::WKBMultiPoint25D;
274  break;
276  wkbType = QGis::WKBMultiLineString25D;
277  break;
278  case QGis::WKBPolygon25D:
279  wkbType = QGis::WKBMultiPolygon25D;
280  break;
281  default:
282  break;
283  }
284  }
285  }
286 
287  QgsVectorLayerImport * writer =
288  new QgsVectorLayerImport( uri, providerKey, fields, wkbType, outputCRS, overwrite, options, progress );
289 
290  // check whether file creation was successful
291  ImportError err = writer->hasError();
292  if ( err != NoError )
293  {
294  if ( errorMessage )
295  *errorMessage = writer->errorMessage();
296  delete writer;
297  return err;
298  }
299 
300  if ( errorMessage )
301  {
302  errorMessage->clear();
303  }
304 
305  QgsAttributeList allAttr = skipAttributeCreation ? QgsAttributeList() : layer->pendingAllAttributesList();
306  QgsFeature fet;
307 
308  QgsFeatureRequest req;
309  if ( wkbType == QGis::WKBNoGeometry )
311  if ( skipAttributeCreation )
313 
314  QgsFeatureIterator fit = layer->getFeatures( req );
315 
316  const QgsFeatureIds& ids = layer->selectedFeaturesIds();
317 
318  // Create our transform
319  if ( destCRS )
320  {
321  ct = new QgsCoordinateTransform( layer->crs(), *destCRS );
322  }
323 
324  // Check for failure
325  if ( ct == NULL )
326  {
327  shallTransform = false;
328  }
329 
330  int n = 0;
331 
332  if ( errorMessage )
333  {
334  *errorMessage = QObject::tr( "Feature write errors:" );
335  }
336 
337  if ( progress )
338  {
339  progress->setRange( 0, layer->featureCount() );
340  }
341 
342  // write all features
343  while ( fit.nextFeature( fet ) )
344  {
345  if ( progress && progress->wasCanceled() )
346  {
347  if ( errorMessage )
348  {
349  *errorMessage += "\n" + QObject::tr( "Import was canceled at %1 of %2" ).arg( progress->value() ).arg( progress->maximum() );
350  }
351  break;
352  }
353 
354  if ( writer->errorCount() > 1000 )
355  {
356  if ( errorMessage )
357  {
358  *errorMessage += "\n" + QObject::tr( "Stopping after %1 errors" ).arg( writer->errorCount() );
359  }
360  break;
361  }
362 
363  if ( onlySelected && !ids.contains( fet.id() ) )
364  continue;
365 
366  if ( shallTransform )
367  {
368  try
369  {
370  if ( fet.geometry() )
371  {
372  fet.geometry()->transform( *ct );
373  }
374  }
375  catch ( QgsCsException &e )
376  {
377  delete ct;
378  delete writer;
379 
380  QString msg = QObject::tr( "Failed to transform a point while drawing a feature with ID '%1'. Writing stopped. (Exception: %2)" )
381  .arg( fet.id() ).arg( e.what() );
382  QgsMessageLog::logMessage( msg, QObject::tr( "Vector import" ) );
383  if ( errorMessage )
384  *errorMessage += "\n" + msg;
385 
386  return ErrProjection;
387  }
388  }
389  if ( skipAttributeCreation )
390  {
391  fet.initAttributes( 0 );
392  }
393  if ( !writer->addFeature( fet ) )
394  {
395  if ( writer->hasError() && errorMessage )
396  {
397  *errorMessage += "\n" + writer->errorMessage();
398  }
399  }
400  n++;
401 
402  if ( progress )
403  {
404  progress->setValue( n );
405  }
406  }
407 
408  // flush the buffer to be sure that all features are written
409  if ( !writer->flushBuffer() )
410  {
411  if ( writer->hasError() && errorMessage )
412  {
413  *errorMessage += "\n" + writer->errorMessage();
414  }
415  }
416  int errors = writer->errorCount();
417 
418  if ( !writer->createSpatialIndex() )
419  {
420  if ( writer->hasError() && errorMessage )
421  {
422  *errorMessage += "\n" + writer->errorMessage();
423  }
424  }
425 
426  delete writer;
427 
428  if ( shallTransform )
429  {
430  delete ct;
431  }
432 
433  if ( errorMessage )
434  {
435  if ( errors > 0 )
436  {
437  *errorMessage += "\n" + QObject::tr( "Only %1 of %2 features written." ).arg( n - errors ).arg( n );
438  }
439  else
440  {
441  errorMessage->clear();
442  }
443  }
444 
445  return errors == 0 ? NoError : ErrFeatureWriteFailed;
446 }
QgsFeatureId id() const
Get the feature id for this feature.
Definition: qgsfeature.cpp:100
Wrapper for iterator of features from vector data provider or vector layer.
QMap< int, int > mOldToNewAttrIdx
map attribute indexes to new field indexes
QgsVectorLayerImport(const QString &uri, const QString &provider, const QgsFields &fields, QGis::WkbType geometryType, const QgsCoordinateReferenceSystem *crs, bool overwrite=false, const QMap< QString, QVariant > *options=0, QProgressDialog *progress=0)
create a empty layer and add fields to it
static QgsProviderRegistry * instance(QString pluginPath=QString::null)
means of accessing canonical single instance
#define QgsDebugMsg(str)
Definition: qgslogger.h:36
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeature.h:325
#define FEATURE_BUFFER_SIZE
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest())
Query the provider for features specified in request.
QgsGeometry * geometry() const
Get the geometry object associated with this feature.
Definition: qgsfeature.cpp:112
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: qgsfield.h:161
bool setAttribute(int field, const QVariant &attr)
Set an attribute by id.
Definition: qgsfeature.cpp:190
QStringList errors()
Get recorded errors.
WkbType
Used for symbology operations.
Definition: qgis.h:53
QgsVectorLayerImport::ImportError createEmptyLayer_t(const QString &uri, const QgsFields &fields, QGis::WkbType geometryType, const QgsCoordinateReferenceSystem *destCRS, bool overwrite, QMap< int, int > *oldToNewAttrIdx, QString *errorMessage, const QMap< QString, QVariant > *options)
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:113
virtual bool addFeatures(QgsFeatureList &flist)
Adds a list of features.
static ImportError importLayer(QgsVectorLayer *layer, const QString &uri, const QString &providerKey, const QgsCoordinateReferenceSystem *destCRS, bool onlySelected=false, QString *errorMessage=0, bool skipAttributeCreation=false, QMap< QString, QVariant > *options=0, QProgressDialog *progress=0)
Write contents of vector layer to a different datasource.
static void logMessage(QString message, QString tag=QString::null, MessageLevel level=WARNING)
add a message to the instance (and create it if necessary)
QGis::WkbType wkbType() const
Returns the WKBType or WKBUnknown in case of error.
allows creation of spatial index
virtual bool createSpatialIndex()
Creates a spatial index on the datasource (if supported by the provider type).
void setGeometry(const QgsGeometry &geom)
Set this feature's geometry from another QgsGeometry object (deep copy)
Definition: qgsfeature.cpp:134
A convenience class for writing vector files to disk.
QgsDataProvider * provider(const QString &providerKey, const QString &dataSource)
Create an instance of the provider.
const QgsFeatureIds & selectedFeaturesIds() const
Return reference to identifiers of selected features.
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:37
virtual int capabilities() const
Returns a bitmask containing the supported capabilities Note, some capabilities may change depending ...
void initAttributes(int fieldCount)
Initialize this feature with the given number of fields.
Definition: qgsfeature.cpp:181
QString errorMessage()
retrieves error message
This class wraps a request for features to a vector layer (or directly its vector data provider)...
QList< int > QgsAttributeList
const QgsAttributes & attributes() const
Definition: qgsfeature.h:142
~QgsVectorLayerImport()
close the new created layer
void clearErrors()
Clear recorded errors.
int count() const
Return number of items.
Definition: qgsfield.h:195
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
QLibrary * providerLibrary(const QString &providerKey) const
QString providerType() const
Return the provider type for this layer.
QString what() const
Definition: qgsexception.h:35
virtual long featureCount() const
Number of features in the layer.
QgsAttributeList pendingAllAttributesList()
returns list of attributes
ImportError hasError()
checks whether there were any errors
QVector< QVariant > QgsAttributes
Definition: qgsfeature.h:100
virtual bool isValid()=0
Returns true if this is a valid layer.
Class for storing a coordinate reference system (CRS)
Class for doing transforms between two map coordinate systems.
int transform(const QgsCoordinateTransform &ct)
Transform this geometry as described by CoordinateTranasform ct.
const QgsCoordinateReferenceSystem & crs() const
Returns layer's spatial reference system.
void(*)() cast_to_fptr(void *p)
Definition: qgis.h:301
Custom exception class for Coordinate Reference System related exceptions.
const QgsFields & pendingFields() const
returns field list in the to-be-committed state
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.
Represents a vector layer which manages a vector based data sets.
QgsFeatureRequest & setFlags(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.
#define tr(sourceText)