29void QgsJoinByLocationAlgorithm::initAlgorithm( 
const QVariantMap & )
 
   33  auto predicateParam = std::make_unique<QgsProcessingParameterEnum>( QStringLiteral( 
"PREDICATE" ), QObject::tr( 
"Features they (geometric predicate)" ), translatedPredicates(), 
true, 0 );
 
   34  QVariantMap predicateMetadata;
 
   35  QVariantMap widgetMetadata;
 
   36  widgetMetadata.insert( QStringLiteral( 
"useCheckBoxes" ), 
true );
 
   37  widgetMetadata.insert( QStringLiteral( 
"columns" ), 2 );
 
   38  predicateMetadata.insert( QStringLiteral( 
"widget_wrapper" ), widgetMetadata );
 
   39  predicateParam->setMetadata( predicateMetadata );
 
   40  addParameter( predicateParam.release() );
 
   44  QStringList joinMethods;
 
   45  joinMethods << QObject::tr( 
"Create separate feature for each matching feature (one-to-many)" )
 
   46              << QObject::tr( 
"Take attributes of the first matching feature only (one-to-one)" )
 
   47              << QObject::tr( 
"Take attributes of the feature with largest overlap only (one-to-one)" );
 
   48  addParameter( 
new QgsProcessingParameterEnum( QStringLiteral( 
"METHOD" ), QObject::tr( 
"Join type" ), joinMethods, 
false, 
static_cast<int>( OneToMany ) ) );
 
   49  addParameter( 
new QgsProcessingParameterBoolean( QStringLiteral( 
"DISCARD_NONMATCHING" ), QObject::tr( 
"Discard records which could not be joined" ), 
false ) );
 
   50  addParameter( 
new QgsProcessingParameterString( QStringLiteral( 
"PREFIX" ), QObject::tr( 
"Joined field prefix" ), QVariant(), 
false, 
true ) );
 
   53  addOutput( 
new QgsProcessingOutputNumber( QStringLiteral( 
"JOINED_COUNT" ), QObject::tr( 
"Number of joined features from input table" ) ) );
 
   56QString QgsJoinByLocationAlgorithm::name()
 const 
   58  return QStringLiteral( 
"joinattributesbylocation" );
 
   61QString QgsJoinByLocationAlgorithm::displayName()
 const 
   63  return QObject::tr( 
"Join attributes by location" );
 
   66QStringList QgsJoinByLocationAlgorithm::tags()
 const 
   68  return QObject::tr( 
"join,intersects,intersecting,touching,within,contains,overlaps,relation,spatial" ).split( 
',' );
 
   71QString QgsJoinByLocationAlgorithm::group()
 const 
   73  return QObject::tr( 
"Vector general" );
 
   76QString QgsJoinByLocationAlgorithm::groupId()
 const 
   78  return QStringLiteral( 
"vectorgeneral" );
 
   81QString QgsJoinByLocationAlgorithm::shortHelpString()
 const 
   83  return QObject::tr( 
"This algorithm takes an input vector layer and creates a new vector layer " 
   84                      "that is an extended version of the input one, with additional attributes in its attribute table.\n\n" 
   85                      "The additional attributes and their values are taken from a second vector layer. " 
   86                      "A spatial criteria is applied to select the values from the second layer that are added " 
   87                      "to each feature from the first layer in the resulting one." );
 
   90QString QgsJoinByLocationAlgorithm::shortDescription()
 const 
   92  return QObject::tr( 
"Joins attributes from one vector layer to another by location." );
 
  100QgsJoinByLocationAlgorithm *QgsJoinByLocationAlgorithm::createInstance()
 const 
  102  return new QgsJoinByLocationAlgorithm();
 
  105QStringList QgsJoinByLocationAlgorithm::translatedPredicates()
 
  107  return { QObject::tr( 
"intersect" ), QObject::tr( 
"contain" ), QObject::tr( 
"equal" ), QObject::tr( 
"touch" ), QObject::tr( 
"overlap" ), QObject::tr( 
"are within" ), QObject::tr( 
"cross" ) };
 
  112  mBaseSource.reset( parameterAsSource( parameters, QStringLiteral( 
"INPUT" ), context ) );
 
  116  mJoinSource.reset( parameterAsSource( parameters, QStringLiteral( 
"JOIN" ), context ) );
 
  120  mJoinMethod = 
static_cast<JoinMethod
>( parameterAsEnum( parameters, QStringLiteral( 
"METHOD" ), context ) );
 
  122  const QStringList joinedFieldNames = parameterAsStrings( parameters, QStringLiteral( 
"JOIN_FIELDS" ), context );
 
  124  mPredicates = parameterAsEnums( parameters, QStringLiteral( 
"PREDICATE" ), context );
 
  125  sortPredicates( mPredicates );
 
  127  QString prefix = parameterAsString( parameters, QStringLiteral( 
"PREFIX" ), context );
 
  130  if ( joinedFieldNames.empty() )
 
  132    joinFields = mJoinSource->fields();
 
  137    mJoinedFieldIndices.reserve( joinedFieldNames.count() );
 
  138    for ( 
const QString &field : joinedFieldNames )
 
  140      int index = mJoinSource->fields().lookupField( field );
 
  143        mJoinedFieldIndices << index;
 
  144        joinFields.
append( mJoinSource->fields().at( index ) );
 
  149  if ( !prefix.isEmpty() )
 
  151    for ( 
int i = 0; i < joinFields.
count(); ++i )
 
  153      joinFields.
rename( i, prefix + joinFields[i].name() );
 
  159  QString joinedSinkId;
 
  160  mJoinedFeatures.reset( parameterAsSink( parameters, QStringLiteral( 
"OUTPUT" ), context, joinedSinkId, outputFields, mBaseSource->wkbType(), mBaseSource->sourceCrs(), 
QgsFeatureSink::RegeneratePrimaryKey ) );
 
  162  if ( parameters.value( QStringLiteral( 
"OUTPUT" ) ).isValid() && !mJoinedFeatures )
 
  165  mDiscardNonMatching = parameterAsBoolean( parameters, QStringLiteral( 
"DISCARD_NONMATCHING" ), context );
 
  167  QString nonMatchingSinkId;
 
  168  mUnjoinedFeatures.reset( parameterAsSink( parameters, QStringLiteral( 
"NON_MATCHING" ), context, nonMatchingSinkId, mBaseSource->fields(), mBaseSource->wkbType(), mBaseSource->sourceCrs(), 
QgsFeatureSink::RegeneratePrimaryKey ) );
 
  169  if ( parameters.value( QStringLiteral( 
"NON_MATCHING" ) ).isValid() && !mUnjoinedFeatures )
 
  172  switch ( mJoinMethod )
 
  177      if ( mBaseSource->featureCount() > 0 && mJoinSource->featureCount() > 0 && mBaseSource->featureCount() < mJoinSource->featureCount() )
 
  180        processAlgorithmByIteratingOverInputSource( context, feedback );
 
  190        processAlgorithmByIteratingOverJoinedSource( context, feedback );
 
  195    case JoinToLargestOverlap:
 
  196      processAlgorithmByIteratingOverInputSource( context, feedback );
 
  201  if ( mJoinedFeatures )
 
  203    mJoinedFeatures->finalize();
 
  204    outputs.insert( QStringLiteral( 
"OUTPUT" ), joinedSinkId );
 
  206  if ( mUnjoinedFeatures )
 
  208    mUnjoinedFeatures->finalize();
 
  209    outputs.insert( QStringLiteral( 
"NON_MATCHING" ), nonMatchingSinkId );
 
  213  mJoinedFeatures.reset();
 
  214  mUnjoinedFeatures.reset();
 
  216  outputs.insert( QStringLiteral( 
"JOINED_COUNT" ), 
static_cast<long long>( mJoinedCount ) );
 
  220bool QgsJoinByLocationAlgorithm::featureFilter( 
const QgsFeature &feature, 
QgsGeometryEngine *engine, 
bool comparingToJoinedFeature, 
const QList<int> &predicates )
 
  224  for ( 
const int predicate : predicates )
 
  237        if ( comparingToJoinedFeature )
 
  246          if ( engine->
within( geom ) )
 
  275        if ( comparingToJoinedFeature )
 
  277          if ( engine->
within( geom ) )
 
  307    feedback->
pushWarning( QObject::tr( 
"No spatial index exists for input layer, performance will be severely degraded" ) );
 
  313  const double step = mJoinSource->featureCount() > 0 ? 100.0 / mJoinSource->featureCount() : 1;
 
  320    processFeatureFromJoinSource( f, feedback );
 
  326  if ( !mDiscardNonMatching || mUnjoinedFeatures )
 
  329    unjoinedIds.subtract( mAddedIds );
 
  336    emptyAttributes.reserve( mJoinedFieldIndices.count() );
 
  337    for ( 
int i = 0; i < mJoinedFieldIndices.count(); ++i )
 
  338      emptyAttributes << QVariant();
 
  345      if ( mJoinedFeatures && !mDiscardNonMatching )
 
  348        attributes.append( emptyAttributes );
 
  350        outputFeature.setAttributes( attributes );
 
  352          throw QgsProcessingException( writeFeatureError( mJoinedFeatures.get(), QVariantMap(), QStringLiteral( 
"OUTPUT" ) ) );
 
  355      if ( mUnjoinedFeatures )
 
  358          throw QgsProcessingException( writeFeatureError( mUnjoinedFeatures.get(), QVariantMap(), QStringLiteral( 
"NON_MATCHING" ) ) );
 
  367    feedback->
pushWarning( QObject::tr( 
"No spatial index exists for join layer, performance will be severely degraded" ) );
 
  372  const double step = mBaseSource->featureCount() > 0 ? 100.0 / mBaseSource->featureCount() : 1;
 
  379    processFeatureFromInputSource( f, context, feedback );
 
  386void QgsJoinByLocationAlgorithm::sortPredicates( QList<int> &predicates )
 
  393  std::sort( predicates.begin(), predicates.end(), []( 
int a, 
int b ) -> 
bool {
 
  417  std::unique_ptr<QgsGeometryEngine> engine;
 
  429    switch ( mJoinMethod )
 
  432        if ( mAddedIds.contains( baseFeature.
id() ) )
 
  442      case JoinToLargestOverlap:
 
  443        Q_ASSERT_X( 
false, 
"QgsJoinByLocationAlgorithm::processFeatureFromJoinSource", 
"processFeatureFromJoinSource should not be used with join to largest overlap method" );
 
  449      engine->prepareGeometry();
 
  450      for ( 
int ix : std::as_const( mJoinedFieldIndices ) )
 
  452        joinAttributes.append( joinFeature.
attribute( ix ) );
 
  455    if ( featureFilter( baseFeature, engine.get(), 
false, mPredicates ) )
 
  457      if ( mJoinedFeatures )
 
  460        outputFeature.setAttributes( baseFeature.
attributes() + joinAttributes );
 
  462          throw QgsProcessingException( writeFeatureError( mJoinedFeatures.get(), QVariantMap(), QStringLiteral( 
"OUTPUT" ) ) );
 
  467      mAddedIds.insert( baseFeature.
id() );
 
  479    if ( mJoinedFeatures && !mDiscardNonMatching )
 
  482      emptyAttributes.reserve( mJoinedFieldIndices.count() );
 
  483      for ( 
int i = 0; i < mJoinedFieldIndices.count(); ++i )
 
  484        emptyAttributes << QVariant();
 
  487      attributes.append( emptyAttributes );
 
  489      outputFeature.setAttributes( attributes );
 
  491        throw QgsProcessingException( writeFeatureError( mJoinedFeatures.get(), QVariantMap(), QStringLiteral( 
"OUTPUT" ) ) );
 
  494    if ( mUnjoinedFeatures )
 
  497        throw QgsProcessingException( writeFeatureError( mUnjoinedFeatures.get(), QVariantMap(), QStringLiteral( 
"NON_MATCHING" ) ) );
 
  504  std::unique_ptr<QgsGeometryEngine> engine;
 
  511  double largestOverlap = std::numeric_limits<double>::lowest();
 
  522      engine->prepareGeometry();
 
  525    if ( featureFilter( joinFeature, engine.get(), 
true, mPredicates ) )
 
  527      switch ( mJoinMethod )
 
  531          if ( mJoinedFeatures )
 
  534            joinAttributes.reserve( joinAttributes.size() + mJoinedFieldIndices.size() );
 
  535            for ( 
int ix : std::as_const( mJoinedFieldIndices ) )
 
  537              joinAttributes.append( joinFeature.
attribute( ix ) );
 
  541            outputFeature.setAttributes( joinAttributes );
 
  543              throw QgsProcessingException( writeFeatureError( mJoinedFeatures.get(), QVariantMap(), QStringLiteral( 
"OUTPUT" ) ) );
 
  547        case JoinToLargestOverlap:
 
  550          std::unique_ptr<QgsAbstractGeometry> intersection( engine->intersection( joinFeature.
geometry().
constGet() ) );
 
  555              overlap = intersection->length();
 
  559              overlap = intersection->area();
 
  568          if ( overlap > largestOverlap )
 
  570            largestOverlap = overlap;
 
  571            bestMatch = joinFeature;
 
  579      if ( mJoinMethod == JoinToFirst )
 
  584  switch ( mJoinMethod )
 
  590    case JoinToLargestOverlap:
 
  595        if ( mJoinedFeatures )
 
  598          joinAttributes.reserve( joinAttributes.size() + mJoinedFieldIndices.size() );
 
  599          for ( 
int ix : std::as_const( mJoinedFieldIndices ) )
 
  601            joinAttributes.append( bestMatch.
attribute( ix ) );
 
  605          outputFeature.setAttributes( joinAttributes );
 
  607            throw QgsProcessingException( writeFeatureError( mJoinedFeatures.get(), QVariantMap(), QStringLiteral( 
"OUTPUT" ) ) );
 
  621    if ( mJoinedFeatures && !mDiscardNonMatching )
 
  624      emptyAttributes.reserve( mJoinedFieldIndices.count() );
 
  625      for ( 
int i = 0; i < mJoinedFieldIndices.count(); ++i )
 
  626        emptyAttributes << QVariant();
 
  629      attributes.append( emptyAttributes );
 
  631      outputFeature.setAttributes( attributes );
 
  633        throw QgsProcessingException( writeFeatureError( mJoinedFeatures.get(), QVariantMap(), QStringLiteral( 
"OUTPUT" ) ) );
 
  636    if ( mUnjoinedFeatures )
 
  639        throw QgsProcessingException( writeFeatureError( mUnjoinedFeatures.get(), QVariantMap(), QStringLiteral( 
"NON_MATCHING" ) ) );
 
@ VectorAnyGeometry
Any vector layer with geometry.
 
@ NotPresent
No spatial index exists for the source.
 
@ RegeneratesPrimaryKey
Algorithm always drops any existing primary keys or FID values and regenerates them in outputs.
 
QFlags< ProcessingAlgorithmDocumentationFlag > ProcessingAlgorithmDocumentationFlags
Flags describing algorithm behavior for documentation purposes.
 
Abstract base class for all geometries.
 
Wrapper for iterator of features from vector data provider or vector layer.
 
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
 
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...
 
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.
 
Q_INVOKABLE 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.
 
bool append(const QgsField &field, Qgis::FieldOrigin origin=Qgis::FieldOrigin::Provider, int originIndex=-1)
Appends a field.
 
QgsAttributeList allAttributesList() const
Utility function to get list of attribute indexes.
 
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.
 
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
 
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry, double precision=0.0, Qgis::GeosCreationFlags flags=Qgis::GeosCreationFlag::SkipEmptyInteriorRings)
Creates and returns a new geometry engine representing the specified geometry using precision on a gr...
 
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).
 
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