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