29 void QgsJoinByLocationAlgorithm::initAlgorithm(
const QVariantMap & )
32 QObject::tr(
"Base Layer" ), QList< int > () << QgsProcessing::QgsProcessing::TypeVectorAnyGeometry ) );
34 QObject::tr(
"Join Layer" ), QList< int > () << QgsProcessing::QgsProcessing::TypeVectorAnyGeometry ) );
36 QStringList predicates;
37 predicates << QObject::tr(
"intersects" )
38 << QObject::tr(
"contains" )
39 << QObject::tr(
"equals" )
40 << QObject::tr(
"touches" )
41 << QObject::tr(
"overlaps" )
42 << QObject::tr(
"within" )
43 << QObject::tr(
"crosses" );
45 std::unique_ptr< QgsProcessingParameterEnum > predicateParam = qgis::make_unique< QgsProcessingParameterEnum >( QStringLiteral(
"PREDICATE" ), QObject::tr(
"Geometric predicate" ), predicates,
true, 0 );
46 QVariantMap predicateMetadata;
47 QVariantMap widgetMetadata;
48 widgetMetadata.insert( QStringLiteral(
"useCheckBoxes" ),
true );
49 widgetMetadata.insert( QStringLiteral(
"columns" ), 2 );
50 predicateMetadata.insert( QStringLiteral(
"widget_wrapper" ), widgetMetadata );
51 predicateParam->setMetadata( predicateMetadata );
52 addParameter( predicateParam.release() );
54 QObject::tr(
"Fields to add (leave empty to use all fields)" ),
57 QStringList joinMethods;
58 joinMethods << QObject::tr(
"Create separate feature for each matching feature (one-to-many)" )
59 << QObject::tr(
"Take attributes of the first matching feature only (one-to-one)" )
60 << QObject::tr(
"Take attributes of the feature with largest overlap only (one-to-one)" );
62 QObject::tr(
"Join type" ),
63 joinMethods,
false, static_cast< int >( OneToMany ) ) );
65 QObject::tr(
"Discard records which could not be joined" ),
68 QObject::tr(
"Joined field prefix" ), QVariant(),
false,
true ) );
71 addOutput(
new QgsProcessingOutputNumber( QStringLiteral(
"JOINED_COUNT" ), QObject::tr(
"Number of joined features from input table" ) ) );
74 QString QgsJoinByLocationAlgorithm::name()
const 76 return QStringLiteral(
"joinattributesbylocation" );
79 QString QgsJoinByLocationAlgorithm::displayName()
const 81 return QObject::tr(
"Join attributes by location" );
84 QStringList QgsJoinByLocationAlgorithm::tags()
const 86 return QObject::tr(
"join,intersects,intersecting,touching,within,contains,overlaps,relation,spatial" ).split(
',' );
89 QString QgsJoinByLocationAlgorithm::group()
const 91 return QObject::tr(
"Vector general" );
94 QString QgsJoinByLocationAlgorithm::groupId()
const 96 return QStringLiteral(
"vectorgeneral" );
99 QString QgsJoinByLocationAlgorithm::shortHelpString()
const 101 return QObject::tr(
"This algorithm takes an input vector layer and creates a new vector layer " 102 "that is an extended version of the input one, with additional attributes in its attribute table.\n\n" 103 "The additional attributes and their values are taken from a second vector layer. " 104 "A spatial criteria is applied to select the values from the second layer that are added " 105 "to each feature from the first layer in the resulting one." );
108 QString QgsJoinByLocationAlgorithm::shortDescription()
const 110 return QObject::tr(
"Join attributes from one vector layer to another by location." );
113 QgsJoinByLocationAlgorithm *QgsJoinByLocationAlgorithm::createInstance()
const 115 return new QgsJoinByLocationAlgorithm();
121 mBaseSource.reset( parameterAsSource( parameters, QStringLiteral(
"INPUT" ), context ) );
125 mJoinSource.reset( parameterAsSource( parameters, QStringLiteral(
"JOIN" ), context ) );
129 mJoinMethod =
static_cast< JoinMethod
>( parameterAsEnum( parameters, QStringLiteral(
"METHOD" ), context ) );
131 const QStringList joinedFieldNames = parameterAsFields( parameters, QStringLiteral(
"JOIN_FIELDS" ), context );
133 mPredicates = parameterAsEnums( parameters, QStringLiteral(
"PREDICATE" ), context );
134 sortPredicates( mPredicates );
136 QString prefix = parameterAsString( parameters, QStringLiteral(
"PREFIX" ), context );
139 if ( joinedFieldNames.empty() )
141 joinFields = mJoinSource->fields();
146 mJoinedFieldIndices.reserve( joinedFieldNames.count() );
147 for (
const QString &field : joinedFieldNames )
149 int index = mJoinSource->fields().lookupField( field );
152 mJoinedFieldIndices << index;
153 joinFields.
append( mJoinSource->fields().at( index ) );
158 if ( !prefix.isEmpty() )
160 for (
int i = 0; i < joinFields.
count(); ++i )
162 joinFields.
rename( i, prefix + joinFields[ i ].name() );
168 QString joinedSinkId;
169 mJoinedFeatures.reset( parameterAsSink( parameters, QStringLiteral(
"OUTPUT" ), context, joinedSinkId, outputFields,
172 if ( parameters.value( QStringLiteral(
"OUTPUT" ) ).isValid() && !mJoinedFeatures )
175 mDiscardNonMatching = parameterAsBoolean( parameters, QStringLiteral(
"DISCARD_NONMATCHING" ), context );
177 QString nonMatchingSinkId;
178 mUnjoinedFeatures.reset( parameterAsSink( parameters, QStringLiteral(
"NON_MATCHING" ), context, nonMatchingSinkId, mBaseSource->fields(),
180 if ( parameters.value( QStringLiteral(
"NON_MATCHING" ) ).isValid() && !mUnjoinedFeatures )
183 switch ( mJoinMethod )
188 if ( mBaseSource->featureCount() > 0 && mJoinSource->featureCount() > 0 && mBaseSource->featureCount() < mJoinSource->featureCount() )
191 processAlgorithmByIteratingOverInputSource( context, feedback );
201 processAlgorithmByIteratingOverJoinedSource( context, feedback );
206 case JoinToLargestOverlap:
207 processAlgorithmByIteratingOverInputSource( context, feedback );
212 if ( mJoinedFeatures )
214 outputs.insert( QStringLiteral(
"OUTPUT" ), joinedSinkId );
216 if ( mUnjoinedFeatures )
218 outputs.insert( QStringLiteral(
"NON_MATCHING" ), nonMatchingSinkId );
222 mJoinedFeatures.reset();
223 mUnjoinedFeatures.reset();
225 outputs.insert( QStringLiteral(
"JOINED_COUNT" ), static_cast< long long >( mJoinedCount ) );
229 bool QgsJoinByLocationAlgorithm::featureFilter(
const QgsFeature &feature,
QgsGeometryEngine *engine,
bool comparingToJoinedFeature )
const 233 for (
const int predicate : mPredicates )
246 if ( comparingToJoinedFeature )
255 if ( engine->
within( geom ) )
284 if ( comparingToJoinedFeature )
286 if ( engine->
within( geom ) )
316 feedback->
reportError( QObject::tr(
"No spatial index exists for input layer, performance will be severely degraded" ) );
322 const double step = mJoinSource->featureCount() > 0 ? 100.0 / mJoinSource->featureCount() : 1;
329 processFeatureFromJoinSource( f, feedback );
335 if ( !mDiscardNonMatching || mUnjoinedFeatures )
338 unjoinedIds.subtract( mAddedIds );
345 emptyAttributes.reserve( mJoinedFieldIndices.count() );
346 for (
int i = 0; i < mJoinedFieldIndices.count(); ++i )
347 emptyAttributes << QVariant();
354 if ( mJoinedFeatures && !mDiscardNonMatching )
357 attributes.append( emptyAttributes );
359 outputFeature.setAttributes( attributes );
363 if ( mUnjoinedFeatures )
372 feedback->
reportError( QObject::tr(
"No spatial index exists for join layer, performance will be severely degraded" ) );
377 const double step = mBaseSource->featureCount() > 0 ? 100.0 / mBaseSource->featureCount() : 1;
379 while ( it .nextFeature( f ) )
384 processFeatureFromInputSource( f, context, feedback );
391 void QgsJoinByLocationAlgorithm::sortPredicates( QList<int> &predicates )
398 std::sort( predicates.begin(), predicates.end(), [](
int a,
int b ) ->
bool 423 std::unique_ptr< QgsGeometryEngine > engine;
426 QList<QgsFeature> filtered;
436 switch ( mJoinMethod )
439 if ( mAddedIds.contains( baseFeature.
id() ) )
449 case JoinToLargestOverlap:
450 Q_ASSERT_X(
false,
"QgsJoinByLocationAlgorithm::processFeatureFromJoinSource",
"processFeatureFromJoinSource should not be used with join to largest overlap method" );
456 engine->prepareGeometry();
457 for (
int ix : qgis::as_const( mJoinedFieldIndices ) )
459 joinAttributes.append( joinFeature.
attribute( ix ) );
462 if ( featureFilter( baseFeature, engine.get(), false ) )
464 if ( mJoinedFeatures )
467 outputFeature.setAttributes( baseFeature.
attributes() + joinAttributes );
473 mAddedIds.insert( baseFeature.
id() );
485 if ( mJoinedFeatures && !mDiscardNonMatching )
488 emptyAttributes.reserve( mJoinedFieldIndices.count() );
489 for (
int i = 0; i < mJoinedFieldIndices.count(); ++i )
490 emptyAttributes << QVariant();
493 attributes.append( emptyAttributes );
495 outputFeature.setAttributes( attributes );
499 if ( mUnjoinedFeatures )
506 std::unique_ptr< QgsGeometryEngine > engine;
510 QList<QgsFeature> filtered;
514 double largestOverlap = std::numeric_limits< double >::lowest();
525 engine->prepareGeometry();
528 if ( featureFilter( joinFeature, engine.get(), true ) )
530 switch ( mJoinMethod )
534 if ( mJoinedFeatures )
537 joinAttributes.reserve( joinAttributes.size() + mJoinedFieldIndices.size() );
538 for (
int ix : qgis::as_const( mJoinedFieldIndices ) )
540 joinAttributes.append( joinFeature.
attribute( ix ) );
544 outputFeature.setAttributes( joinAttributes );
549 case JoinToLargestOverlap:
552 std::unique_ptr< QgsAbstractGeometry > intersection( engine->intersection( joinFeature.
geometry().
constGet() ) );
557 overlap = intersection->length();
561 overlap = intersection->area();
570 if ( overlap > largestOverlap )
572 largestOverlap = overlap;
573 bestMatch = joinFeature;
581 if ( mJoinMethod == JoinToFirst )
586 switch ( mJoinMethod )
592 case JoinToLargestOverlap:
597 if ( mJoinedFeatures )
600 joinAttributes.reserve( joinAttributes.size() + mJoinedFieldIndices.size() );
601 for (
int ix : qgis::as_const( mJoinedFieldIndices ) )
603 joinAttributes.append( bestMatch.
attribute( ix ) );
607 outputFeature.setAttributes( joinAttributes );
622 if ( mJoinedFeatures && !mDiscardNonMatching )
625 emptyAttributes.reserve( mJoinedFieldIndices.count() );
626 for (
int i = 0; i < mJoinedFieldIndices.count(); ++i )
627 emptyAttributes << QVariant();
630 attributes.append( emptyAttributes );
632 outputFeature.setAttributes( attributes );
636 if ( mUnjoinedFeatures )
A boolean parameter for processing algorithms.
bool isValid() const
Returns the validity of this feature.
QgsFeatureRequest & setDestinationCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets the destination crs for feature's geometries.
Wrapper for iterator of features from vector data provider or vector layer.
No spatial index exists for the source.
Use faster inserts, at the cost of updating the passed features to reflect changes made at the provid...
virtual bool crosses(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const =0
Checks if geom crosses this.
QSet< QgsFeatureId > QgsFeatureIds
Base class for providing feedback from a processing algorithm.
virtual bool within(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const =0
Checks if geom is within this.
A vector layer or feature source field parameter for processing algorithms.
virtual bool touches(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const =0
Checks if geom touches this.
void setProgress(double progress)
Sets the current progress for the feedback object.
Container of fields for a vector layer.
A geometry is the spatial representation of a feature.
A numeric output for processing algorithms.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
bool rename(int fieldIdx, const QString &name)
Renames a name of field.
bool hasGeometry() const
Returns true if the feature has an associated geometry.
int count() const
Returns number of items.
A feature sink output for processing algorithms.
virtual bool intersects(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const =0
Checks if geom intersects this.
This flag indicates, that a primary key field cannot be guaranteed to be unique and the sink should i...
QgsAttributeList allAttributesList() const
Utility function to get list of attribute indexes.
An enum based parameter for processing algorithms, allowing for selection from predefined values...
static GeometryType geometryType(Type type)
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
This class wraps a request for features to a vector layer (or directly its vector data provider)...
Custom exception class for processing related exceptions.
QgsFeatureRequest & setFilterRect(const QgsRectangle &rectangle)
Sets the rectangle from which features will be taken.
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context.
bool append(const QgsField &field, FieldOrigin origin=OriginProvider, int originIndex=-1)
Appends a field. The field must have unique name, otherwise it is rejected (returns false) ...
Abstract base class for all geometries.
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry)
Creates and returns a new geometry engine.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
virtual bool overlaps(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const =0
Checks if geom overlaps this.
QgsFeatureRequest & setFilterFids(const QgsFeatureIds &fids)
Sets feature IDs that should be fetched.
bool isCanceled() const
Tells whether the operation has been canceled already.
An input feature source (such as vector layers) parameter for processing algorithms.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
virtual bool contains(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const =0
Checks if geom contains this.
Contains geometry relation and modification algorithms.
virtual void reportError(const QString &error, bool fatalError=false)
Reports that the algorithm encountered an error while executing.
bool nextFeature(QgsFeature &f)
static QgsFields combineFields(const QgsFields &fieldsA, const QgsFields &fieldsB, const QString &fieldsBPrefix=QString())
Combines two field lists, avoiding duplicate field names (in a case-insensitive manner).
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
Contains information about the context in which a processing algorithm is executed.
A string parameter for processing algorithms.
Any vector layer with geometry.
virtual bool isEqual(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const =0
Checks if this is equal to geom.