QGIS API Documentation  3.0.2-Girona (307d082)
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 
182  bool hasZ = QgsWkbTypes::hasZ( outputType );
183  bool hasM = QgsWkbTypes::hasM( outputType );
184  bool isMulti = QgsWkbTypes::isMultiType( outputType );
185  double step = totalFeatureCount > 0 ? 100.0 / totalFeatureCount : 1;
186  i = 0;
187  for ( QgsMapLayer *layer : layers )
188  {
189  if ( !layer )
190  continue;
191 
192  QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( layer );
193  if ( !vl )
194  continue;
195 
196  feedback->pushInfo( QObject::tr( "Packaging layer %1/%2: %3" ).arg( i ).arg( layers.count() ).arg( layer->name() ) );
197 
198  QgsFeatureIterator it = vl->getFeatures( QgsFeatureRequest().setDestinationCrs( outputCrs, context.transformContext() ) );
199  QgsFeature f;
200  while ( it.nextFeature( f ) )
201  {
202  if ( feedback->isCanceled() )
203  break;
204 
205  // ensure feature geometry is of correct type
206  if ( f.hasGeometry() )
207  {
208  bool changed = false;
209  QgsGeometry g = f.geometry();
210  if ( hasZ && !g.constGet()->is3D() )
211  {
212  g.get()->addZValue( 0 );
213  changed = true;
214  }
215  if ( hasM && !g.constGet()->isMeasure() )
216  {
217  g.get()->addMValue( 0 );
218  changed = true;
219  }
220  if ( isMulti && !g.isMultipart() )
221  {
222  g.convertToMultiType();
223  changed = true;
224  }
225  if ( changed )
226  f.setGeometry( g );
227  }
228 
229  // process feature attributes
230  QgsAttributes destAttributes;
231  for ( const QgsField &destField : outputFields )
232  {
233  if ( addLayerField && destField.name() == QLatin1String( "layer" ) )
234  {
235  destAttributes.append( layer->name() );
236  continue;
237  }
238  else if ( addPathField && destField.name() == QLatin1String( "path" ) )
239  {
240  destAttributes.append( layer->publicSource() );
241  continue;
242  }
243 
244  QVariant destAttribute;
245  int sourceIndex = vl->fields().lookupField( destField.name() );
246  if ( sourceIndex >= 0 )
247  {
248  destAttribute = f.attributes().at( sourceIndex );
249  }
250  destAttributes.append( destAttribute );
251  }
252  f.setAttributes( destAttributes );
253 
254  sink->addFeature( f, QgsFeatureSink::FastInsert );
255  i += 1;
256  feedback->setProgress( i * step );
257  }
258  }
259 
260  if ( errored )
261  throw QgsProcessingException( QObject::tr( "Error obtained while merging one or more layers." ) );
262 
263  QVariantMap outputs;
264  outputs.insert( QStringLiteral( "OUTPUT" ), dest );
265  return outputs;
266 }
267 
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:56
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:111
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)
Return 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:54
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:60
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.