26 #include <QDomElement> 35 QList<QgsVectorLayer *> lst;
44 static bool _hasCycleDFS(
QgsVectorLayer *n, QHash<QgsVectorLayer *, int> &mark )
46 if ( mark.value( n ) == 1 )
48 if ( mark.value( n ) == 0 )
53 if ( _hasCycleDFS( m, mark ) )
64 QMutexLocker locker( &mMutex );
65 mVectorJoins.push_back( joinInfo );
69 QHash<QgsVectorLayer *, int> markDFS;
70 if ( mLayer && _hasCycleDFS( mLayer, markDFS ) )
73 mVectorJoins.pop_back();
80 cacheJoinLayer( mVectorJoins.last() );
89 connectJoinedLayer( vl );
101 QMutexLocker locker( &mMutex );
103 for (
int i = 0; i < mVectorJoins.size(); ++i )
105 if ( mVectorJoins.at( i ).joinLayerId() == joinLayerId )
112 mVectorJoins.removeAt( i );
134 if ( joinFieldIndex < 0 || joinFieldIndex >= cacheLayer->
fields().
count() )
143 QVector<int> subsetIndices;
151 if ( !cacheLayerAttrs.contains( joinFieldIndex ) )
152 cacheLayerAttrs.append( joinFieldIndex );
161 QString key = attrs.at( joinFieldIndex ).toString();
165 for (
int i = 0; i < subsetIndices.count(); ++i )
166 subsetAttrs[i] = attrs.at( subsetIndices.at( i ) );
172 attrs2.remove( joinFieldIndex );
183 QVector<int> subsetIndices;
185 for (
int i = 0; i < joinFieldsSubset.count(); ++i )
187 QString joinedFieldName = joinFieldsSubset.
at( i );
191 subsetIndices.append( index );
195 QgsDebugMsg(
"Join layer subset field not found: " + joinedFieldName );
199 return subsetIndices;
206 QList< QgsVectorLayerJoinInfo>::const_iterator joinIt = mVectorJoins.constBegin();
207 for (
int joinIdx = 0; joinIt != mVectorJoins.constEnd(); ++joinIt, ++joinIdx )
216 QString joinFieldName = joinIt->joinFieldName();
218 QSet<QString> subset;
219 if ( joinIt->hasSubset() )
222 subset = QSet<QString>::fromList( subsetNames );
225 if ( joinIt->prefix().isNull() )
227 prefix = joinLayer->
name() +
'_';
231 prefix = joinIt->prefix();
234 for (
int idx = 0; idx < joinFields.
count(); ++idx )
237 if ( joinIt->hasSubset() && !subset.contains( joinFields.
at( idx ).
name() ) )
242 if ( joinIt->hasSubset() || joinFields.
at( idx ).
name() != joinFieldName )
254 QMutexLocker locker( &mMutex );
255 QList< QgsVectorLayerJoinInfo >::iterator joinIt = mVectorJoins.begin();
256 for ( ; joinIt != mVectorJoins.end(); ++joinIt )
258 if ( joinIt->isUsingMemoryCache() && joinIt->cacheDirty )
259 cacheJoinLayer( *joinIt );
266 QDomElement vectorJoinsElem = document.createElement( QStringLiteral(
"vectorjoins" ) );
267 layer_node.appendChild( vectorJoinsElem );
268 QList< QgsVectorLayerJoinInfo >::const_iterator joinIt = mVectorJoins.constBegin();
269 for ( ; joinIt != mVectorJoins.constEnd(); ++joinIt )
274 QDomElement joinElem = document.createElement( QStringLiteral(
"join" ) );
276 joinElem.setAttribute( QStringLiteral(
"targetFieldName" ), joinIt->targetFieldName() );
278 joinElem.setAttribute( QStringLiteral(
"joinLayerId" ), joinIt->joinLayerId() );
279 joinElem.setAttribute( QStringLiteral(
"joinFieldName" ), joinIt->joinFieldName() );
281 joinElem.setAttribute( QStringLiteral(
"memoryCache" ), joinIt->isUsingMemoryCache() );
282 joinElem.setAttribute( QStringLiteral(
"dynamicForm" ), joinIt->isDynamicFormEnabled() );
283 joinElem.setAttribute( QStringLiteral(
"editable" ), joinIt->isEditable() );
284 joinElem.setAttribute( QStringLiteral(
"upsertOnEdit" ), joinIt->hasUpsertOnEdit() );
285 joinElem.setAttribute( QStringLiteral(
"cascadedDelete" ), joinIt->hasCascadedDelete() );
287 if ( joinIt->hasSubset() )
289 QDomElement subsetElem = document.createElement( QStringLiteral(
"joinFieldsSubset" ) );
292 Q_FOREACH (
const QString &fieldName, subsetNames )
294 QDomElement fieldElem = document.createElement( QStringLiteral(
"field" ) );
295 fieldElem.setAttribute( QStringLiteral(
"name" ), fieldName );
296 subsetElem.appendChild( fieldElem );
299 joinElem.appendChild( subsetElem );
302 if ( !joinIt->prefix().isNull() )
304 joinElem.setAttribute( QStringLiteral(
"customPrefix" ), joinIt->prefix() );
305 joinElem.setAttribute( QStringLiteral(
"hasCustomPrefix" ), 1 );
308 vectorJoinsElem.appendChild( joinElem );
314 mVectorJoins.clear();
315 QDomElement vectorJoinsElem = layer_node.firstChildElement( QStringLiteral(
"vectorjoins" ) );
316 if ( !vectorJoinsElem.isNull() )
318 QDomNodeList joinList = vectorJoinsElem.elementsByTagName( QStringLiteral(
"join" ) );
319 for (
int i = 0; i < joinList.size(); ++i )
321 QDomElement infoElem = joinList.at( i ).toElement();
323 info.
setJoinFieldName( infoElem.attribute( QStringLiteral(
"joinFieldName" ) ) );
325 info.
setJoinLayerId( infoElem.attribute( QStringLiteral(
"joinLayerId" ) ) );
329 info.
setEditable( infoElem.attribute( QStringLiteral(
"editable" ) ).toInt() );
330 info.
setUpsertOnEdit( infoElem.attribute( QStringLiteral(
"upsertOnEdit" ) ).toInt() );
331 info.
setCascadedDelete( infoElem.attribute( QStringLiteral(
"cascadedDelete" ) ).toInt() );
333 QDomElement subsetElem = infoElem.firstChildElement( QStringLiteral(
"joinFieldsSubset" ) );
334 if ( !subsetElem.isNull() )
336 QStringList *fieldNames =
new QStringList;
337 QDomNodeList fieldNodes = infoElem.elementsByTagName( QStringLiteral(
"field" ) );
338 fieldNames->reserve( fieldNodes.count() );
339 for (
int i = 0; i < fieldNodes.count(); ++i )
340 *fieldNames << fieldNodes.at( i ).toElement().attribute( QStringLiteral(
"name" ) );
344 if ( infoElem.attribute( QStringLiteral(
"hasCustomPrefix" ) ).toInt() )
345 info.
setPrefix( infoElem.attribute( QStringLiteral(
"customPrefix" ) ) );
356 bool resolved =
false;
357 for ( QgsVectorJoinList::iterator it = mVectorJoins.begin(); it != mVectorJoins.end(); ++it )
359 if ( it->joinLayer() )
362 if (
QgsVectorLayer *joinedLayer = qobject_cast<QgsVectorLayer *>( project->
mapLayer( it->joinLayerId() ) ) )
364 it->setJoinLayer( joinedLayer );
365 connectJoinedLayer( joinedLayer );
379 int joinIndex = mVectorJoins.indexOf( *info );
380 if ( joinIndex == -1 )
383 for (
int i = 0; i < fields.
count(); ++i )
400 int sourceJoinIndex = originIndex / 1000;
401 sourceFieldIndex = originIndex % 1000;
403 if ( sourceJoinIndex < 0 || sourceJoinIndex >= mVectorJoins.count() )
406 return &( mVectorJoins[sourceJoinIndex] );
411 QList<const QgsVectorLayerJoinInfo *> infos;
415 if ( infos.contains( &info ) )
419 infos.append( &info );
446 return joinedFeature;
466 return targetedFeature;
472 cloned->mVectorJoins = mVectorJoins;
476 void QgsVectorLayerJoinBuffer::joinedLayerUpdatedFields()
482 Q_ASSERT( joinedLayer );
485 for ( QgsVectorJoinList::iterator it = mVectorJoins.begin(); it != mVectorJoins.end(); ++it )
487 if ( joinedLayer == it->joinLayer() )
489 it->cachedAttributes.clear();
490 cacheJoinLayer( *it );
497 void QgsVectorLayerJoinBuffer::joinedLayerModified()
500 Q_ASSERT( joinedLayer );
503 for ( QgsVectorJoinList::iterator it = mVectorJoins.begin(); it != mVectorJoins.end(); ++it )
505 if ( joinedLayer == it->joinLayer() )
507 it->cacheDirty =
true;
512 void QgsVectorLayerJoinBuffer::joinedLayerWillBeDeleted()
515 Q_ASSERT( joinedLayer );
520 void QgsVectorLayerJoinBuffer::connectJoinedLayer(
QgsVectorLayer *vl )
541 Q_FOREACH (
const QgsFeature &feature, features )
560 if ( existingFeature.
isValid() )
565 Q_FOREACH (
const QString &field, subsetNames )
567 QVariant newValue = joinFeature.
attribute( field );
575 for (
const auto &field : joinFields )
577 QVariant newValue = joinFeature.
attribute( field.name() );
578 int fieldIndex = joinLayer->
fields().
indexOf( field.name() );
586 bool notNullFields =
false;
588 for (
const auto &field : joinFields )
593 if ( !joinFeature.
attribute( field.name() ).isNull() )
595 notNullFields =
true;
601 joinFeatures << joinFeature;
644 for (
auto it = newValues.constBegin(); it != newValues.constEnd(); ++it )
646 const int field = it.key();
647 const QVariant newValue = it.value();
650 if ( oldValues.contains( field ) )
651 oldValue = oldValues[field];
bool addFeatures(QgsFeatureList &features, QgsFeatureSink::Flags flags=nullptr) override
Adds a list of features in joined layers.
const QList< QgsVectorLayerJoinInfo > vectorJoins() const
Wrapper for iterator of features from vector data provider or vector layer.
Field comes from a joined layer (originIndex / 1000 = index of the join, originIndex % 1000 = index w...
QSet< QgsFeatureId > QgsFeatureIds
QgsFeature joinedFeatureOf(const QgsVectorLayerJoinInfo *info, const QgsFeature &feature) const
Returns the joined feature corresponding to the feature.
QString joinLayerId() const
ID of the joined layer - may be used to resolve reference to the joined layer.
void setFields(const QgsFields &fields, bool initAttributes=false)
Assign a field map with the feature to allow attribute access by attribute name.
void createJoinCaches()
Calls cacheJoinLayer() for all vector joins.
const QgsVectorLayerJoinInfo * joinForFieldIndex(int index, const QgsFields &fields, int &sourceFieldIndex) const
Finds the vector join for a layer field index.
bool isValid() const
Returns the validity of this feature.
QList< const QgsVectorLayerJoinInfo * > joinsWhereFieldIsId(const QgsField &field) const
Returns joins where the field of a target layer is considered as an id.
void setEditable(bool enabled)
Sets whether the form of the target layer allows editing joined fields.
bool hasUpsertOnEdit() const
Returns whether a feature created on the target layer has to impact the joined layer by creating a ne...
bool deleteFeature(QgsFeatureId fid)
Deletes a feature from the layer (but does not commit it).
Class allowing to manage the auxiliary storage for a vector layer.
QList< QgsFeature > QgsFeatureList
const QgsVectorJoinList & vectorJoins() const
void willBeDeleted()
Emitted in the destructor when the layer is about to be deleted, but it is still in a perfectly valid...
QString targetFieldName() const
Returns name of the field of our layer that will be used for join.
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
void readXml(const QDomNode &layer_node)
Reads joins from project file.
void setJoinFieldName(const QString &fieldName)
Sets name of the field of joined layer that will be used for join.
Container of fields for a vector layer.
void setName(const QString &name)
Set the field name.
bool setAttribute(int field, const QVariant &attr)
Set an attribute's value by field index.
void setDynamicFormEnabled(bool enabled)
Sets whether the form has to be dynamically updated with joined fields when a feature is being create...
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
QgsVectorLayerJoinBuffer(QgsVectorLayer *layer=nullptr)
QgsField at(int i) const
Gets field at particular index (must be in range 0..N-1)
QHash< QString, QgsAttributes > cachedAttributes
Cache for joined attributes to provide fast lookup (size is 0 if no memory caching) ...
Manages joined fields for a vector layer.
QgsVectorLayerJoinBuffer * clone() const
Create a copy of the join buffer.
bool containsJoins() const
Quick way to test if there is any join at all.
bool isUsingMemoryCache() const
Returns whether values from the joined layer should be cached in memory to speed up lookups...
bool isEditable() const
Returns whether joined fields may be edited through the form of the target layer. ...
bool isEditable() const FINAL
Returns true if the provider is in editing mode.
bool changeAttributeValue(QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue=QVariant())
Changes attribute value in joined layers.
bool deleteFeature(QgsFeatureId fid) const
Deletes a feature from joined layers.
void setUpsertOnEdit(bool enabled)
Sets whether a feature created on the target layer has to impact the joined layer by creating a new f...
QgsFeature getFeature(QgsFeatureId fid) const
Query the layer for the feature with the given id.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
QgsFeature extractJoinedFeature(const QgsFeature &feature) const
Extract the join feature from the target feature for the current join layer information.
QStringList * joinFieldNamesSubset() const
Gets subset of fields to be used from joined layer.
bool changeAttributeValues(QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues=QgsAttributeMap())
Changes attributes' values in joined layers.
QgsFeatureRequest & setNoAttributes()
Set that no attributes will be fetched.
void initAttributes(int fieldCount)
Initialize this feature with the given number of fields.
Defines left outer join from our vector layer to some other vector layer.
QMap< int, QVariant > QgsAttributeMap
int fieldOriginIndex(int fieldIdx) const
Gets field's origin index (its meaning is specific to each type of origin)
QString joinFieldName() const
Returns name of the field of joined layer that will be used for join.
This class wraps a request for features to a vector layer (or directly its vector data provider)...
QgsFeature targetedFeatureOf(const QgsVectorLayerJoinInfo *info, const QgsFeature &feature) const
Returns the targeted feature corresponding to the joined feature.
bool removeJoin(const QString &joinLayerId)
Removes a vector layer join.
bool addFeatures(QgsFeatureList &features, QgsFeatureSink::Flags flags=nullptr) FINAL
Adds a list of features to the sink.
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) ...
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
Reads and writes project states.
int count() const
Returns number of items.
void setUsingMemoryCache(bool enabled)
Sets whether values from the joined layer should be cached in memory to speed up lookups.
Encapsulate a field in an attribute table or data source.
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
int indexOf(const QString &fieldName) const
Gets the field index from the field name.
bool cacheDirty
True if the cached join attributes need to be updated.
void setTargetFieldName(const QString &fieldName)
Sets name of the field of our layer that will be used for join.
static QString createFieldEqualityExpression(const QString &fieldName, const QVariant &value)
Create an expression allowing to evaluate if a field is equal to a value.
QgsAuxiliaryLayer * auxiliaryLayer()
Returns the current auxiliary layer.
int indexFromName(const QString &fieldName) const
Gets the field index from the field name.
bool hasSubset(bool blacklisted=true) const
Returns true if blacklisted fields is not empty or if a subset of names has been set.
void setPrefix(const QString &prefix)
Sets prefix of fields from the joined layer. If null, joined layer's name will be used...
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
bool addJoin(const QgsVectorLayerJoinInfo &joinInfo)
Joins another vector layer to this layer.
bool hasCascadedDelete() const
Returns whether a feature deleted on the target layer has to impact the joined layer by deleting the ...
QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
void writeXml(QDomNode &layer_node, QDomDocument &document) const
Saves mVectorJoins to xml under the layer node.
void updateFields(QgsFields &fields)
Updates field map with joined attributes.
static QVector< int > joinSubsetIndices(QgsVectorLayer *joinLayer, const QStringList &joinFieldsSubset)
Returns a vector of indices for use in join based on field names from the layer.
FieldOrigin fieldOrigin(int fieldIdx) const
Gets field's origin (value from an enumeration)
QgsFeatureRequest & setLimit(long limit)
Set the maximum number of features to request.
bool deleteFeatures(const QgsFeatureIds &fids) const
Deletes a list of features from joined layers.
void setCascadedDelete(bool enabled)
Sets whether a feature deleted on the target layer has to impact the joined layer by deleting the cor...
void joinedFieldsChanged()
Emitted whenever the list of joined fields changes (e.g.
QgsVectorLayer * joinLayer() const
Returns joined layer (may be null if the reference was set by layer ID and not resolved yet) ...
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Query the layer for features specified in request.
bool changeAttributeValue(QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue=QVariant(), bool skipDefaultValues=false)
Changes an attribute value for a feature (but does not immediately commit the changes).
QList< int > QgsAttributeList
bool nextFeature(QgsFeature &f)
Geometry is not required. It may still be returned if e.g. required for a filter condition.
Represents a vector layer which manages a vector based data sets.
void updatedFields()
Is emitted, whenever the fields available from this layer have been changed.
void layerModified()
This signal is emitted when modifications has been done on layer.
void setJoinFieldNamesSubset(QStringList *fieldNamesSubset)
Set subset of fields to be used from joined layer.
void resolveReferences(QgsProject *project)
Resolves layer IDs of joined layers using given project's available layers.
void setJoinLayerId(const QString &layerId)
Sets ID of the joined layer. It will need to be overwritten by setJoinLayer() to a reference to real ...
int joinedFieldsOffset(const QgsVectorLayerJoinInfo *info, const QgsFields &fields)
Find out what is the first index of the join within fields.
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.
bool isAuxiliaryJoin(const QgsVectorLayerJoinInfo &info) const
Returns true if the join information is about auxiliary layer, false otherwise.