29 void 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" ) ) );
 
   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 );
 
  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 );
 
  395 void 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 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