QGIS API Documentation  3.0.2-Girona (307d082)
qgsalgorithmjoinbyattribute.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsalgorithmjoinbyattribute.cpp
3  ---------------------
4  begin : April 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 QgsJoinByAttributeAlgorithm::name() const
23 {
24  return QStringLiteral( "joinattributestable" );
25 }
26 
27 QString QgsJoinByAttributeAlgorithm::displayName() const
28 {
29  return QObject::tr( "Join attributes table" );
30 }
31 
32 QStringList QgsJoinByAttributeAlgorithm::tags() const
33 {
34  return QObject::tr( "join,connect,attributes,values,fields" ).split( ',' );
35 }
36 
37 QString QgsJoinByAttributeAlgorithm::group() const
38 {
39  return QObject::tr( "Vector general" );
40 }
41 
42 QString QgsJoinByAttributeAlgorithm::groupId() const
43 {
44  return QStringLiteral( "vectorgeneral" );
45 }
46 
47 void QgsJoinByAttributeAlgorithm::initAlgorithm( const QVariantMap & )
48 {
49  addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ),
50  QObject::tr( "Input layer" ), QList< int>() << QgsProcessing::TypeVector ) );
51  addParameter( new QgsProcessingParameterField( QStringLiteral( "FIELD" ),
52  QObject::tr( "Table field" ), QVariant(), QStringLiteral( "INPUT" ) ) );
53 
54  addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT_2" ),
55  QObject::tr( "Input layer 2" ), QList< int>() << QgsProcessing::TypeVector ) );
56  addParameter( new QgsProcessingParameterField( QStringLiteral( "FIELD_2" ),
57  QObject::tr( "Table field 2" ), QVariant(), QStringLiteral( "INPUT_2" ) ) );
58 
59  addParameter( new QgsProcessingParameterField( QStringLiteral( "FIELDS_TO_COPY" ),
60  QObject::tr( "Layer 2 fields to copy (leave empty to copy all fields)" ),
61  QVariant(), QStringLiteral( "INPUT_2" ), QgsProcessingParameterField::Any,
62  true, true ) );
63 
64  addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Joined layer" ) ) );
65 }
66 
67 QString QgsJoinByAttributeAlgorithm::shortHelpString() const
68 {
69  return QObject::tr( "This algorithm takes an input vector layer and creates a new vector layer that is an extended version of the "
70  "input one, with additional attributes in its attribute table.\n\n"
71  "The additional attributes and their values are taken from a second vector layer. An attribute is selected "
72  "in each of them to define the join criteria." );
73 }
74 
75 QgsJoinByAttributeAlgorithm *QgsJoinByAttributeAlgorithm::createInstance() const
76 {
77  return new QgsJoinByAttributeAlgorithm();
78 }
79 
80 QVariantMap QgsJoinByAttributeAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
81 {
82  std::unique_ptr< QgsFeatureSource > input( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
83  std::unique_ptr< QgsFeatureSource > input2( parameterAsSource( parameters, QStringLiteral( "INPUT_2" ), context ) );
84  if ( !input || !input2 )
85  throw QgsProcessingException( QObject::tr( "Could not load source layers" ) );
86 
87  QString field1Name = parameterAsString( parameters, QStringLiteral( "FIELD" ), context );
88  QString field2Name = parameterAsString( parameters, QStringLiteral( "FIELD_2" ), context );
89  const QStringList fieldsToCopy = parameterAsFields( parameters, QStringLiteral( "FIELDS_TO_COPY" ), context );
90 
91  int joinField1Index = input->fields().lookupField( field1Name );
92  int joinField2Index = input2->fields().lookupField( field2Name );
93  if ( joinField1Index < 0 || joinField2Index < 0 )
94  throw QgsProcessingException( QObject::tr( "Invalid join fields" ) );
95 
96  QgsFields outFields2;
97  QgsAttributeList fields2Indices;
98  if ( fieldsToCopy.empty() )
99  {
100  outFields2 = input2->fields();
101  for ( int i = 0; i < outFields2.count(); ++i )
102  {
103  fields2Indices << i;
104  }
105  }
106  else
107  {
108  for ( const QString &field : fieldsToCopy )
109  {
110  int index = input2->fields().lookupField( field );
111  if ( index >= 0 )
112  {
113  fields2Indices << index;
114  outFields2.append( input2->fields().at( index ) );
115  }
116  }
117  }
118 
119  QgsAttributeList fields2Fetch = fields2Indices;
120  fields2Fetch << joinField2Index;
121 
122  QgsFields outFields = QgsProcessingUtils::combineFields( input->fields(), outFields2 );
123 
124  QString dest;
125  std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, outFields,
126  input->wkbType(), input->sourceCrs() ) );
127  if ( !sink )
128  throw QgsProcessingException( QObject::tr( "Could not create destination layer for OUTPUT" ) );
129 
130 
131  // cache attributes of input2
132  QHash< QVariant, QgsAttributes > input2AttributeCache;
133  QgsFeatureIterator features = input2->getFeatures( QgsFeatureRequest().setFlags( QgsFeatureRequest::NoGeometry ).setSubsetOfAttributes( fields2Fetch ) );
134  double step = input2->featureCount() > 0 ? 50.0 / input2->featureCount() : 1;
135  int i = 0;
136  QgsFeature feat;
137  while ( features.nextFeature( feat ) )
138  {
139  i++;
140  if ( feedback->isCanceled() )
141  {
142  break;
143  }
144 
145  feedback->setProgress( i * step );
146 
147  if ( input2AttributeCache.contains( feat.attribute( joinField2Index ) ) )
148  continue;
149 
150  // only keep selected attributes
151  QgsAttributes attributes;
152  for ( int j = 0; j < feat.attributes().count(); ++j )
153  {
154  if ( ! fields2Indices.contains( j ) )
155  continue;
156  attributes << feat.attribute( j );
157  }
158 
159  input2AttributeCache.insert( feat.attribute( joinField2Index ), attributes );
160  }
161 
162  // Create output vector layer with additional attribute
163  step = input->featureCount() > 0 ? 50.0 / input->featureCount() : 1;
164  features = input->getFeatures();
165  i = 0;
166  while ( features.nextFeature( feat ) )
167  {
168  i++;
169  if ( feedback->isCanceled() )
170  {
171  break;
172  }
173 
174  feedback->setProgress( 50 + i * step );
175 
176  QgsAttributes attrs = feat.attributes();
177  attrs.append( input2AttributeCache.value( feat.attribute( joinField1Index ) ) );
178  feat.setAttributes( attrs );
179  sink->addFeature( feat, QgsFeatureSink::FastInsert );
180  }
181 
182  QVariantMap outputs;
183  outputs.insert( QStringLiteral( "OUTPUT" ), dest );
184  return outputs;
185 }
186 
187 
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.
Use faster inserts, at the cost of updating the passed features to reflect changes made at the provid...
Base class for providing feedback from a processing algorithm.
A vector layer or feature source field parameter for processing algorithms.
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
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
int count() const
Return number of items.
Definition: qgsfields.cpp:115
A feature sink output for processing algorithms.
static QgsFields combineFields(const QgsFields &fieldsA, const QgsFields &fieldsB)
Combines two field lists, avoiding duplicate field names (in a case-insensitive manner).
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
bool append(const QgsField &field, FieldOrigin origin=OriginProvider, int originIndex=-1)
Append a field. The field must have unique name, otherwise it is rejected (returns false) ...
Definition: qgsfields.cpp:59
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:54
An input feature source (such as vector layers) parameter for processing algorithms.
Tables (i.e. vector layers with or without geometry). When used for a sink this indicates the sink ha...
Definition: qgsprocessing.h:54
QList< int > QgsAttributeList
Definition: qgsfield.h:27
bool nextFeature(QgsFeature &f)
Geometry is not required. It may still be returned if e.g. required for a filter condition.
A vector of attributes.
Definition: qgsattributes.h:58
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
Definition: qgsfeature.cpp:255
Contains information about the context in which a processing algorithm is executed.
QgsAttributes attributes
Definition: qgsfeature.h:72