29void QgsJoinByLocationAlgorithm::initAlgorithm(
const QVariantMap & )
32 QObject::tr(
"Join to features in" ), QList< int > () << QgsProcessing::QgsProcessing::TypeVectorAnyGeometry ) );
34 QStringList predicates;
35 predicates << QObject::tr(
"intersect" )
36 << QObject::tr(
"contain" )
37 << QObject::tr(
"equal" )
38 << QObject::tr(
"touch" )
39 << QObject::tr(
"overlap" )
40 << QObject::tr(
"are within" )
41 << QObject::tr(
"cross" );
43 std::unique_ptr< QgsProcessingParameterEnum > predicateParam = std::make_unique< QgsProcessingParameterEnum >( QStringLiteral(
"PREDICATE" ), QObject::tr(
"Features they (geometric predicate)" ), predicates,
true, 0 );
44 QVariantMap predicateMetadata;
45 QVariantMap widgetMetadata;
46 widgetMetadata.insert( QStringLiteral(
"useCheckBoxes" ),
true );
47 widgetMetadata.insert( QStringLiteral(
"columns" ), 2 );
48 predicateMetadata.insert( QStringLiteral(
"widget_wrapper" ), widgetMetadata );
49 predicateParam->setMetadata( predicateMetadata );
50 addParameter( predicateParam.release() );
52 QObject::tr(
"By comparing to" ), QList< int > () << QgsProcessing::QgsProcessing::TypeVectorAnyGeometry ) );
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" ) ) );
74QString QgsJoinByLocationAlgorithm::name()
const
76 return QStringLiteral(
"joinattributesbylocation" );
79QString QgsJoinByLocationAlgorithm::displayName()
const
81 return QObject::tr(
"Join attributes by location" );
84QStringList QgsJoinByLocationAlgorithm::tags()
const
86 return QObject::tr(
"join,intersects,intersecting,touching,within,contains,overlaps,relation,spatial" ).split(
',' );
89QString QgsJoinByLocationAlgorithm::group()
const
91 return QObject::tr(
"Vector general" );
94QString QgsJoinByLocationAlgorithm::groupId()
const
96 return QStringLiteral(
"vectorgeneral" );
99QString 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." );
108QString QgsJoinByLocationAlgorithm::shortDescription()
const
110 return QObject::tr(
"Join attributes from one vector layer to another by location." );
113QgsJoinByLocationAlgorithm *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 = parameterAsStrings( 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 ) );
229bool 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->
pushWarning( 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 );
361 throw QgsProcessingException( writeFeatureError( mJoinedFeatures.get(), QVariantMap(), QStringLiteral(
"OUTPUT" ) ) );
364 if ( mUnjoinedFeatures )
367 throw QgsProcessingException( writeFeatureError( mUnjoinedFeatures.get(), QVariantMap(), QStringLiteral(
"NON_MATCHING" ) ) );
376 feedback->
pushWarning( QObject::tr(
"No spatial index exists for join layer, performance will be severely degraded" ) );
381 const double step = mBaseSource->featureCount() > 0 ? 100.0 / mBaseSource->featureCount() : 1;
383 while ( it .nextFeature( f ) )
388 processFeatureFromInputSource( f, context, feedback );
395void QgsJoinByLocationAlgorithm::sortPredicates( QList<int> &predicates )
402 std::sort( predicates.begin(), predicates.end(), [](
int a,
int b ) ->
bool
427 std::unique_ptr< QgsGeometryEngine > engine;
439 switch ( mJoinMethod )
442 if ( mAddedIds.contains( baseFeature.
id() ) )
452 case JoinToLargestOverlap:
453 Q_ASSERT_X(
false,
"QgsJoinByLocationAlgorithm::processFeatureFromJoinSource",
"processFeatureFromJoinSource should not be used with join to largest overlap method" );
459 engine->prepareGeometry();
460 for (
int ix : std::as_const( mJoinedFieldIndices ) )
462 joinAttributes.append( joinFeature.
attribute( ix ) );
465 if ( featureFilter( baseFeature, engine.get(),
false ) )
467 if ( mJoinedFeatures )
470 outputFeature.setAttributes( baseFeature.
attributes() + joinAttributes );
472 throw QgsProcessingException( writeFeatureError( mJoinedFeatures.get(), QVariantMap(), QStringLiteral(
"OUTPUT" ) ) );
477 mAddedIds.insert( baseFeature.
id() );
489 if ( mJoinedFeatures && !mDiscardNonMatching )
492 emptyAttributes.reserve( mJoinedFieldIndices.count() );
493 for (
int i = 0; i < mJoinedFieldIndices.count(); ++i )
494 emptyAttributes << QVariant();
497 attributes.append( emptyAttributes );
499 outputFeature.setAttributes( attributes );
501 throw QgsProcessingException( writeFeatureError( mJoinedFeatures.get(), QVariantMap(), QStringLiteral(
"OUTPUT" ) ) );
504 if ( mUnjoinedFeatures )
507 throw QgsProcessingException( writeFeatureError( mUnjoinedFeatures.get(), QVariantMap(), QStringLiteral(
"NON_MATCHING" ) ) );
514 std::unique_ptr< QgsGeometryEngine > engine;
521 double largestOverlap = std::numeric_limits< double >::lowest();
532 engine->prepareGeometry();
535 if ( featureFilter( joinFeature, engine.get(),
true ) )
537 switch ( mJoinMethod )
541 if ( mJoinedFeatures )
544 joinAttributes.reserve( joinAttributes.size() + mJoinedFieldIndices.size() );
545 for (
int ix : std::as_const( mJoinedFieldIndices ) )
547 joinAttributes.append( joinFeature.
attribute( ix ) );
551 outputFeature.setAttributes( joinAttributes );
553 throw QgsProcessingException( writeFeatureError( mJoinedFeatures.get(), QVariantMap(), QStringLiteral(
"OUTPUT" ) ) );
557 case JoinToLargestOverlap:
560 std::unique_ptr< QgsAbstractGeometry > intersection( engine->intersection( joinFeature.
geometry().
constGet() ) );
565 overlap = intersection->length();
569 overlap = intersection->area();
578 if ( overlap > largestOverlap )
580 largestOverlap = overlap;
581 bestMatch = joinFeature;
589 if ( mJoinMethod == JoinToFirst )
594 switch ( mJoinMethod )
600 case JoinToLargestOverlap:
605 if ( mJoinedFeatures )
608 joinAttributes.reserve( joinAttributes.size() + mJoinedFieldIndices.size() );
609 for (
int ix : std::as_const( mJoinedFieldIndices ) )
611 joinAttributes.append( bestMatch.
attribute( ix ) );
615 outputFeature.setAttributes( joinAttributes );
617 throw QgsProcessingException( writeFeatureError( mJoinedFeatures.get(), QVariantMap(), QStringLiteral(
"OUTPUT" ) ) );
631 if ( mJoinedFeatures && !mDiscardNonMatching )
634 emptyAttributes.reserve( mJoinedFieldIndices.count() );
635 for (
int i = 0; i < mJoinedFieldIndices.count(); ++i )
636 emptyAttributes << QVariant();
639 attributes.append( emptyAttributes );
641 outputFeature.setAttributes( attributes );
643 throw QgsProcessingException( writeFeatureError( mJoinedFeatures.get(), QVariantMap(), QStringLiteral(
"OUTPUT" ) ) );
646 if ( mUnjoinedFeatures )
649 throw QgsProcessingException( writeFeatureError( mUnjoinedFeatures.get(), QVariantMap(), QStringLiteral(
"NON_MATCHING" ) ) );
Abstract base class for all geometries.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFilterFids(const QgsFeatureIds &fids)
Sets the feature IDs that should be fetched.
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
QgsFeatureRequest & setDestinationCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets the destination crs for feature's geometries.
QgsFeatureRequest & setFilterRect(const QgsRectangle &rectangle)
Sets the rectangle from which features will be taken.
@ FastInsert
Use faster inserts, at the cost of updating the passed features to reflect changes made at the provid...
@ RegeneratePrimaryKey
This flag indicates, that a primary key field cannot be guaranteed to be unique and the sink should i...
@ SpatialIndexNotPresent
No spatial index exists for the source.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
bool hasGeometry() const
Returns true if the feature has an associated geometry.
bool isValid() const
Returns the validity of this feature.
QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
bool isCanceled() const
Tells whether the operation has been canceled already.
void setProgress(double progress)
Sets the current progress for the feedback object.
Container of fields for a vector layer.
QgsAttributeList allAttributesList() const
Utility function to get list of attribute indexes.
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)
int count() const
Returns number of items.
bool rename(int fieldIdx, const QString &name)
Renames a name of field.
A geometry engine is a low-level representation of a QgsAbstractGeometry object, optimised for use wi...
virtual bool isEqual(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const =0
Checks if this is equal to geom.
virtual bool intersects(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const =0
Checks if geom intersects this.
virtual bool touches(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const =0
Checks if geom touches this.
virtual bool crosses(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const =0
Checks if geom crosses this.
virtual bool overlaps(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const =0
Checks if geom overlaps this.
virtual bool within(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const =0
Checks if geom is within this.
virtual bool contains(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const =0
Checks if geom contains this.
A geometry is the spatial representation of a feature.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry)
Creates and returns a new geometry engine representing the specified geometry.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
Contains information about the context in which a processing algorithm is executed.
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context.
Custom exception class for processing related exceptions.
Base class for providing feedback from a processing algorithm.
virtual void pushWarning(const QString &warning)
Pushes a warning informational message from the algorithm.
A numeric output for processing algorithms.
A boolean parameter for processing algorithms.
An enum based parameter for processing algorithms, allowing for selection from predefined values.
A feature sink output for processing algorithms.
An input feature source (such as vector layers) parameter for processing algorithms.
A vector layer or feature source field parameter for processing algorithms.
A string parameter for processing algorithms.
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).
@ TypeVectorAnyGeometry
Any vector layer with geometry.
static Qgis::GeometryType geometryType(Qgis::WkbType type)
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
QSet< QgsFeatureId > QgsFeatureIds