23 QString QgsJoinByAttributeAlgorithm::name()
const
25 return QStringLiteral(
"joinattributestable" );
28 QString QgsJoinByAttributeAlgorithm::displayName()
const
30 return QObject::tr(
"Join attributes by field value" );
33 QStringList QgsJoinByAttributeAlgorithm::tags()
const
35 return QObject::tr(
"join,connect,attributes,values,fields,tables" ).split(
',' );
38 QString QgsJoinByAttributeAlgorithm::group()
const
40 return QObject::tr(
"Vector general" );
43 QString QgsJoinByAttributeAlgorithm::groupId()
const
45 return QStringLiteral(
"vectorgeneral" );
48 void QgsJoinByAttributeAlgorithm::initAlgorithm(
const QVariantMap & )
51 methods << QObject::tr(
"Create separate feature for each matching feature (one-to-many)" )
52 << QObject::tr(
"Take attributes of the first matching feature only (one-to-one)" );
57 QObject::tr(
"Table field" ), QVariant(), QStringLiteral(
"INPUT" ) ) );
62 QObject::tr(
"Table field 2" ), QVariant(), QStringLiteral(
"INPUT_2" ) ) );
65 QObject::tr(
"Layer 2 fields to copy (leave empty to copy all fields)" ),
70 QObject::tr(
"Join type" ),
71 methods,
false, 1 ) );
73 QObject::tr(
"Discard records which could not be joined" ),
77 QObject::tr(
"Joined field prefix" ), QVariant(),
false,
true ) );
81 std::unique_ptr< QgsProcessingParameterFeatureSink > nonMatchingSink = std::make_unique< QgsProcessingParameterFeatureSink >(
85 addParameter( nonMatchingSink.release() );
87 addOutput(
new QgsProcessingOutputNumber( QStringLiteral(
"JOINED_COUNT" ), QObject::tr(
"Number of joined features from input table" ) ) );
88 addOutput(
new QgsProcessingOutputNumber( QStringLiteral(
"UNJOINABLE_COUNT" ), QObject::tr(
"Number of unjoinable features from input table" ) ) );
91 QString QgsJoinByAttributeAlgorithm::shortHelpString()
const
93 return QObject::tr(
"This algorithm takes an input vector layer and creates a new vector layer that is an extended version of the "
94 "input one, with additional attributes in its attribute table.\n\n"
95 "The additional attributes and their values are taken from a second vector layer. An attribute is selected "
96 "in each of them to define the join criteria." );
99 QgsJoinByAttributeAlgorithm *QgsJoinByAttributeAlgorithm::createInstance()
const
101 return new QgsJoinByAttributeAlgorithm();
106 const int joinMethod = parameterAsEnum( parameters, QStringLiteral(
"METHOD" ), context );
107 const bool discardNonMatching = parameterAsBoolean( parameters, QStringLiteral(
"DISCARD_NONMATCHING" ), context );
109 std::unique_ptr< QgsProcessingFeatureSource > input( parameterAsSource( parameters, QStringLiteral(
"INPUT" ), context ) );
113 std::unique_ptr< QgsProcessingFeatureSource > input2( parameterAsSource( parameters, QStringLiteral(
"INPUT_2" ), context ) );
117 const QString prefix = parameterAsString( parameters, QStringLiteral(
"PREFIX" ), context );
119 const QString field1Name = parameterAsString( parameters, QStringLiteral(
"FIELD" ), context );
120 const QString field2Name = parameterAsString( parameters, QStringLiteral(
"FIELD_2" ), context );
121 const QStringList fieldsToCopy = parameterAsFields( parameters, QStringLiteral(
"FIELDS_TO_COPY" ), context );
123 const int joinField1Index = input->fields().lookupField( field1Name );
124 const int joinField2Index = input2->fields().lookupField( field2Name );
125 if ( joinField1Index < 0 || joinField2Index < 0 )
130 if ( fieldsToCopy.empty() )
132 outFields2 = input2->fields();
133 fields2Indices.reserve( outFields2.
count() );
134 for (
int i = 0; i < outFields2.
count(); ++i )
141 fields2Indices.reserve( fieldsToCopy.count() );
142 for (
const QString &
field : fieldsToCopy )
147 fields2Indices << index;
148 outFields2.
append( input2->fields().at( index ) );
153 if ( !prefix.isEmpty() )
155 for (
int i = 0; i < outFields2.
count(); ++i )
157 outFields2.
rename( i, prefix + outFields2[ i ].name() );
162 fields2Fetch << joinField2Index;
167 std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral(
"OUTPUT" ), context, dest, outFields,
169 if ( parameters.value( QStringLiteral(
"OUTPUT" ) ).isValid() && !sink )
172 QString destNonMatching1;
173 std::unique_ptr< QgsFeatureSink > sinkNonMatching1( parameterAsSink( parameters, QStringLiteral(
"NON_MATCHING" ), context, destNonMatching1, input->fields(),
175 if ( parameters.value( QStringLiteral(
"NON_MATCHING" ) ).isValid() && !sinkNonMatching1 )
179 QMultiHash< QVariant, QgsAttributes > input2AttributeCache;
181 double step = input2->featureCount() > 0 ? 50.0 / input2->featureCount() : 1;
194 if ( joinMethod == 1 && input2AttributeCache.contains( feat.
attribute( joinField2Index ) ) )
199 for (
int j = 0; j < feat.
attributes().count(); ++j )
201 if ( ! fields2Indices.contains( j ) )
206 input2AttributeCache.insert( feat.
attribute( joinField2Index ), attributes );
210 step = input->featureCount() > 0 ? 50.0 / input->featureCount() : 1;
213 long long joinedCount = 0;
214 long long unjoinedCount = 0;
225 if ( input2AttributeCache.count( feat.
attribute( joinField1Index ) ) > 0 )
232 QList< QgsAttributes > attributes = input2AttributeCache.values( feat.
attribute( joinField1Index ) );
233 QList< QgsAttributes >::iterator attrsIt = attributes.begin();
234 for ( ; attrsIt != attributes.end(); ++attrsIt )
237 newAttrs.append( *attrsIt );
247 if ( sink && !discardNonMatching )
252 if ( sinkNonMatching1 )
255 throw QgsProcessingException( writeFeatureError( sinkNonMatching1.get(), parameters, QStringLiteral(
"NON_MATCHING" ) ) );
261 feedback->
pushInfo( QObject::tr(
"%n feature(s) from input layer were successfully matched",
nullptr, joinedCount ) );
262 if ( unjoinedCount > 0 )
263 feedback->
reportError( QObject::tr(
"%n feature(s) from input layer could not be matched",
nullptr, unjoinedCount ) );
267 outputs.insert( QStringLiteral(
"OUTPUT" ), dest );
268 outputs.insert( QStringLiteral(
"JOINED_COUNT" ), joinedCount );
269 outputs.insert( QStringLiteral(
"UNJOINABLE_COUNT" ), unjoinedCount );
270 if ( sinkNonMatching1 )
271 outputs.insert( QStringLiteral(
"NON_MATCHING" ), destNonMatching1 );