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 = std::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->
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 );
 
  363       if ( mUnjoinedFeatures )
 
  372     feedback->
pushWarning( 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 : std::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 : std::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 : std::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 )
 
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 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 SIP_HOLDGIL
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 SIP_HOLDGIL
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 GeometryType geometryType(Type type) SIP_HOLDGIL
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
QSet< QgsFeatureId > QgsFeatureIds