QGIS API Documentation  3.2.0-Bonn (bc43194)
qgsalgorithmmergevector.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsalgorithmmergevector.cpp
3  ------------------
4  begin : December 2017
5  copyright : (C) 2017 by Nyall Dawson
6  email : nyall dot dawson at gmail dot 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 
19 
21 
22 QString QgsMergeVectorAlgorithm::name() const
23 {
24  return QStringLiteral( "mergevectorlayers" );
25 }
26 
27 QString QgsMergeVectorAlgorithm::displayName() const
28 {
29  return QObject::tr( "Merge vector layers" );
30 }
31 
32 QStringList QgsMergeVectorAlgorithm::tags() const
33 {
34  return QObject::tr( "vector,layers,collect,merge,combine" ).split( ',' );
35 }
36 
37 QString QgsMergeVectorAlgorithm::group() const
38 {
39  return QObject::tr( "Vector general" );
40 }
41 
42 QString QgsMergeVectorAlgorithm::groupId() const
43 {
44  return QStringLiteral( "vectorgeneral" );
45 }
46 
47 void QgsMergeVectorAlgorithm::initAlgorithm( const QVariantMap & )
48 {
49  addParameter( new QgsProcessingParameterMultipleLayers( QStringLiteral( "LAYERS" ), QObject::tr( "Input layers" ), QgsProcessing::TypeVector ) );
50  addParameter( new QgsProcessingParameterCrs( QStringLiteral( "CRS" ), QObject::tr( "Destination CRS" ), QVariant(), true ) );
51  addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Merged" ) ) );
52 }
53 
54 QString QgsMergeVectorAlgorithm::shortHelpString() const
55 {
56  return QObject::tr( "This algorithm combines multiple vector layers of the same geometry type into a single one.\n\n"
57  "If attributes tables are different, the attribute table of the resulting layer will contain the attributes "
58  "from all input layers. New attributes will be added for the original layer name and source.\n\n"
59  "If any input layers contain Z or M values, then the output layer will also contain these values. Similarly, "
60  "if any of the input layers are multi-part, the output layer will also be a multi-part layer.\n\n"
61  "Optionally, the destination coordinate reference system (CRS) for the merged layer can be set. If it is not set, the CRS will be "
62  "taken from the first input layer. All layers will all be reprojected to match this CRS." );
63 }
64 
65 QgsMergeVectorAlgorithm *QgsMergeVectorAlgorithm::createInstance() const
66 {
67  return new QgsMergeVectorAlgorithm();
68 }
69 
70 QVariantMap QgsMergeVectorAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
71 {
72  const QList< QgsMapLayer * > layers = parameterAsLayerList( parameters, QStringLiteral( "LAYERS" ), context );
73 
74  QgsFields outputFields;
75  long totalFeatureCount = 0;
77  QgsCoordinateReferenceSystem outputCrs = parameterAsCrs( parameters, QStringLiteral( "CRS" ), context );
78 
79  if ( outputCrs.isValid() )
80  feedback->pushInfo( QObject::tr( "Using specified destination CRS %1" ).arg( outputCrs.authid() ) );
81 
82  bool errored = false;
83 
84  // loop through input layers and determine geometry type, crs, fields, total feature count,...
85  long i = 0;
86  for ( QgsMapLayer *layer : layers )
87  {
88  i++;
89 
90  if ( feedback->isCanceled() )
91  break;
92 
93  if ( !layer )
94  {
95  feedback->pushDebugInfo( QObject::tr( "Error retrieving map layer." ) );
96  errored = true;
97  continue;
98  }
99 
100  if ( layer->type() != QgsMapLayer::VectorLayer )
101  throw QgsProcessingException( QObject::tr( "All layers must be vector layers!" ) );
102 
103  QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( layer );
104 
105  if ( !outputCrs.isValid() && vl->crs().isValid() )
106  {
107  outputCrs = vl->crs();
108  feedback->pushInfo( QObject::tr( "Taking destination CRS %1 from layer" ).arg( outputCrs.authid() ) );
109  }
110 
111  // check wkb type
112  if ( outputType != QgsWkbTypes::Unknown && outputType != QgsWkbTypes::NoGeometry )
113  {
114  if ( QgsWkbTypes::geometryType( outputType ) != QgsWkbTypes::geometryType( vl->wkbType() ) )
115  throw QgsProcessingException( QObject::tr( "All layers must have same geometry type! Encountered a %1 layer when expecting a %2 layer." )
118 
119  if ( QgsWkbTypes::hasM( vl->wkbType() ) && !QgsWkbTypes::hasM( outputType ) )
120  {
121  outputType = QgsWkbTypes::addM( outputType );
122  feedback->pushInfo( QObject::tr( "Found a layer with M values, upgrading output type to %1" ).arg( QgsWkbTypes::displayString( outputType ) ) );
123  }
124  if ( QgsWkbTypes::hasZ( vl->wkbType() ) && !QgsWkbTypes::hasZ( outputType ) )
125  {
126  outputType = QgsWkbTypes::addZ( outputType );
127  feedback->pushInfo( QObject::tr( "Found a layer with Z values, upgrading output type to %1" ).arg( QgsWkbTypes::displayString( outputType ) ) );
128  }
129  if ( QgsWkbTypes::isMultiType( vl->wkbType() ) && !QgsWkbTypes::isMultiType( outputType ) )
130  {
131  outputType = QgsWkbTypes::multiType( outputType );
132  feedback->pushInfo( QObject::tr( "Found a layer with multiparts, upgrading output type to %1" ).arg( QgsWkbTypes::displayString( outputType ) ) );
133  }
134  }
135  else
136  {
137  outputType = vl->wkbType();
138  feedback->pushInfo( QObject::tr( "Setting output type to %1" ).arg( QgsWkbTypes::displayString( outputType ) ) );
139  }
140 
141  totalFeatureCount += vl->featureCount();
142 
143  // check field type
144  for ( const QgsField &sourceField : vl->fields() )
145  {
146  bool found = false;
147  for ( const QgsField &destField : outputFields )
148  {
149  if ( destField.name().compare( sourceField.name(), Qt::CaseInsensitive ) == 0 )
150  {
151  found = true;
152  if ( destField.type() != sourceField.type() )
153  {
154  throw QgsProcessingException( QObject::tr( "%1 field in layer %2 has different data type than in other layers (%3 instead of %4)" )
155  .arg( sourceField.name(), vl->name(), sourceField.typeName(), destField.typeName() ) );
156  }
157  break;
158  }
159  }
160 
161  if ( !found )
162  outputFields.append( sourceField );
163  }
164  }
165 
166  bool addLayerField = false;
167  if ( outputFields.lookupField( QStringLiteral( "layer" ) ) < 0 )
168  {
169  outputFields.append( QgsField( QStringLiteral( "layer" ), QVariant::String, QString(), 100 ) );
170  addLayerField = true;
171  }
172  bool addPathField = false;
173  if ( outputFields.lookupField( QStringLiteral( "path" ) ) < 0 )
174  {
175  outputFields.append( QgsField( QStringLiteral( "path" ), QVariant::String, QString(), 200 ) );
176  addPathField = true;
177  }
178 
179  QString dest;
180  std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, outputFields, outputType, outputCrs ) );
181  if ( !sink )
182  throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );
183 
184  bool hasZ = QgsWkbTypes::hasZ( outputType );
185  bool hasM = QgsWkbTypes::hasM( outputType );
186  bool isMulti = QgsWkbTypes::isMultiType( outputType );
187  double step = totalFeatureCount > 0 ? 100.0 / totalFeatureCount : 1;
188  i = 0;
189  int layerNumber = 0;
190  for ( QgsMapLayer *layer : layers )
191  {
192  layerNumber++;
193  if ( !layer )
194  continue;
195 
196  QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( layer );
197  if ( !vl )
198  continue;
199 
200  feedback->pushInfo( QObject::tr( "Packaging layer %1/%2: %3" ).arg( layerNumber ).arg( layers.count() ).arg( layer->name() ) );
201 
202  QgsFeatureIterator it = vl->getFeatures( QgsFeatureRequest().setDestinationCrs( outputCrs, context.transformContext() ) );
203  QgsFeature f;
204  while ( it.nextFeature( f ) )
205  {
206  if ( feedback->isCanceled() )
207  break;
208 
209  // ensure feature geometry is of correct type
210  if ( f.hasGeometry() )
211  {
212  bool changed = false;
213  QgsGeometry g = f.geometry();
214  if ( hasZ && !g.constGet()->is3D() )
215  {
216  g.get()->addZValue( 0 );
217  changed = true;
218  }
219  if ( hasM && !g.constGet()->isMeasure() )
220  {
221  g.get()->addMValue( 0 );
222  changed = true;
223  }
224  if ( isMulti && !g.isMultipart() )
225  {
226  g.convertToMultiType();
227  changed = true;
228  }
229  if ( changed )
230  f.setGeometry( g );
231  }
232 
233  // process feature attributes
234  QgsAttributes destAttributes;
235  for ( const QgsField &destField : outputFields )
236  {
237  if ( addLayerField && destField.name() == QLatin1String( "layer" ) )
238  {
239  destAttributes.append( layer->name() );
240  continue;
241  }
242  else if ( addPathField && destField.name() == QLatin1String( "path" ) )
243  {
244  destAttributes.append( layer->publicSource() );
245  continue;
246  }
247 
248  QVariant destAttribute;
249  int sourceIndex = vl->fields().lookupField( destField.name() );
250  if ( sourceIndex >= 0 )
251  {
252  destAttribute = f.attributes().at( sourceIndex );
253  }
254  destAttributes.append( destAttribute );
255  }
256  f.setAttributes( destAttributes );
257 
258  sink->addFeature( f, QgsFeatureSink::FastInsert );
259  i += 1;
260  feedback->setProgress( i * step );
261  }
262  }
263 
264  if ( errored )
265  throw QgsProcessingException( QObject::tr( "Error obtained while merging one or more layers." ) );
266 
267  QVariantMap outputs;
268  outputs.insert( QStringLiteral( "OUTPUT" ), dest );
269  return outputs;
270 }
271 
bool isMeasure() const
Returns true if the geometry contains m values.
int lookupField(const QString &fieldName) const
Look up field&#39;s index from the field name.
Definition: qgsfields.cpp:299
Wrapper for iterator of features from vector data provider or vector layer.
A parameter for processing algorithms which accepts multiple map layers.
Use faster inserts, at the cost of updating the passed features to reflect changes made at the provid...
Base class for all map layer types.
Definition: qgsmaplayer.h:61
static Type multiType(Type type)
Returns the multi type for a WKB type.
Definition: qgswkbtypes.h:296
Base class for providing feedback from a processing algorithm.
bool isMultipart() const
Returns true if WKB of the geometry is of WKBMulti* type.
static bool isMultiType(Type type)
Returns true if the WKB type is a multi type.
Definition: qgswkbtypes.h:557
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition: qgsfeedback.h:63
Container of fields for a vector layer.
Definition: qgsfields.h:42
virtual bool addMValue(double mValue=0)=0
Adds a measure to the geometry, initialized to a preset value.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:104
void setAttributes(const QgsAttributes &attrs)
Sets the feature&#39;s attributes.
Definition: qgsfeature.cpp:127
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:62
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:190
static bool hasZ(Type type)
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:768
A feature sink output for processing algorithms.
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:67
static Type addM(Type type)
Adds the m dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:889
QgsFields fields() const override
Returns the list of fields of this layer.
long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
static QString geometryDisplayString(GeometryType type)
Returns a display string for a geometry type.
QgsWkbTypes::Type wkbType() const override
Returns the WKBType or WKBUnknown in case of error.
static GeometryType geometryType(Type type)
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
Definition: qgswkbtypes.h:663
static Type addZ(Type type)
Adds the z dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:864
This class wraps a request for features to a vector layer (or directly its vector data provider)...
Custom exception class for processing related exceptions.
Definition: qgsexception.h:82
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context.
QgsCoordinateReferenceSystem crs() const
Returns the layer&#39;s spatial reference system.
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:48
QgsGeometry geometry() const
Returns the geometry associated with this feature.
Definition: qgsfeature.cpp:101
A coordinate reference system parameter for processing algorithms.
virtual void pushDebugInfo(const QString &info)
Pushes an informational message containing debugging helpers from the algorithm.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const override
Query the layer for features specified in request.
QgsAbstractGeometry * get()
Returns a modifiable (non-const) reference to the underlying abstract geometry primitive.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
bool convertToMultiType()
Converts single type geometry into multitype geometry e.g.
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:54
virtual bool addZValue(double zValue=0)=0
Adds a z-dimension to the geometry, initialized to a preset value.
This class represents a coordinate reference system (CRS).
void setGeometry(const QgsGeometry &geometry)
Set the feature&#39;s geometry.
Definition: qgsfeature.cpp:137
Tables (i.e. vector layers with or without geometry). When used for a sink this indicates the sink ha...
Definition: qgsprocessing.h:53
static QString displayString(Type type)
Returns a display string type for a WKB type, e.g., the geometry name used in WKT geometry representa...
QString name
Definition: qgsmaplayer.h:65
static bool hasM(Type type)
Tests whether a WKB type contains m values.
Definition: qgswkbtypes.h:818
bool nextFeature(QgsFeature &f)
A vector of attributes.
Definition: qgsattributes.h:58
Represents a vector layer which manages a vector based data sets.
Contains information about the context in which a processing algorithm is executed.
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
QString authid() const
Returns the authority identifier for the CRS.
QgsAttributes attributes
Definition: qgsfeature.h:72
virtual void pushInfo(const QString &info)
Pushes a general informational message from the algorithm.
bool isValid() const
Returns whether this CRS is correctly initialized and usable.