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];
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
bool addFeatures(QgsFeatureList &features, QgsFeatureSink::Flags flags=nullptr) override
Adds a list of features in joined layers.
bool hasSubset(bool blacklisted=true) const
Returns true if blacklisted fields is not empty or if a subset of names has been set.
bool isValid() const
Returns the validity of this feature.
void writeXml(QDomNode &layer_node, QDomDocument &document) const
Saves mVectorJoins to xml under the layer node.
Wrapper for iterator of features from vector data provider or vector layer.
QString targetFieldName() const
Returns name of the field of our layer that will be used for join.
bool containsJoins() const
Quick way to test if there is any join at all.
Field comes from a joined layer (originIndex / 1000 = index of the join, originIndex % 1000 = index w...
QSet< QgsFeatureId > QgsFeatureIds
FieldOrigin fieldOrigin(int fieldIdx) const
Gets field's origin (value from an enumeration)
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.
void setEditable(bool enabled)
Sets whether the form of the target layer allows editing joined fields.
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
void willBeDeleted()
Emitted in the destructor when the layer is about to be deleted, but it is still in a perfectly valid...
QgsFeature getFeature(QgsFeatureId fid) const
Query the layer for the feature with the given id.
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)
int count() const
Returns number of items.
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.
QgsField at(int i) const
Gets field at particular index (must be in range 0..N-1)
int fieldOriginIndex(int fieldIdx) const
Gets field's origin index (its meaning is specific to each type of origin)
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.
void setUpsertOnEdit(bool enabled)
Sets whether a feature created on the target layer has to impact the joined layer by creating a new f...
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
int indexFromName(const QString &fieldName) const
Gets the field index from the field name.
const QList< QgsVectorLayerJoinInfo > vectorJoins() const
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
QgsVectorLayer * joinLayer() const
Returns joined layer (may be null if the reference was set by layer ID and not resolved yet) ...
bool deleteFeature(QgsFeatureId fid) const
Deletes a feature from joined layers.
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.
bool deleteFeatures(const QgsFeatureIds &fids) const
Deletes a list of features from joined layers.
Defines left outer join from our vector layer to some other vector layer.
QMap< int, QVariant > QgsAttributeMap
This class wraps a request for features to a vector layer (or directly its vector data provider)...
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) ...
Reads and writes project states.
void setUsingMemoryCache(bool enabled)
Sets whether values from the joined layer should be cached in memory to speed up lookups.
const QgsVectorJoinList & vectorJoins() const
QList< const QgsVectorLayerJoinInfo * > joinsWhereFieldIsId(const QgsField &field) const
Returns joins where the field of a target layer is considered as an id.
Encapsulate a field in an attribute table or data source.
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.
bool isAuxiliaryJoin(const QgsVectorLayerJoinInfo &info) const
Returns true if the join information is about auxiliary layer, false otherwise.
void setPrefix(const QString &prefix)
Sets prefix of fields from the joined layer. If null, joined layer's name will be used...
QgsFeature targetedFeatureOf(const QgsVectorLayerJoinInfo *info, const QgsFeature &feature) const
Returns the targeted feature corresponding to the joined feature.
bool addJoin(const QgsVectorLayerJoinInfo &joinInfo)
Joins another vector layer to this layer.
int indexOf(const QString &fieldName) const
Gets the field index from the field name.
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.
const QgsVectorLayerJoinInfo * joinForFieldIndex(int index, const QgsFields &fields, int &sourceFieldIndex) const
Finds the vector join for a layer field index.
QgsFeatureRequest & setLimit(long limit)
Set the maximum number of features to request.
QgsFeature joinedFeatureOf(const QgsVectorLayerJoinInfo *info, const QgsFeature &feature) const
Returns the joined feature corresponding to the feature.
QgsFeature extractJoinedFeature(const QgsFeature &feature) const
Extract the join feature from the target feature for the current join layer information.
bool isUsingMemoryCache() const
Returns whether values from the joined layer should be cached in memory to speed up lookups...
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.
QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
bool hasCascadedDelete() const
Returns whether a feature deleted on the target layer has to impact the joined layer by deleting the ...
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 isEditable() const
Returns whether joined fields may be edited through the form of the target layer. ...
bool hasUpsertOnEdit() const
Returns whether a feature created on the target layer has to impact the joined layer by creating a ne...
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.
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
void updatedFields()
Is emitted, whenever the fields available from this layer have been changed.
QString joinFieldName() const
Returns name of the field of joined layer that will be used for join.
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.
QStringList * joinFieldNamesSubset() const
Gets 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.
QgsVectorLayerJoinBuffer * clone() const
Create a copy of the join buffer.
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.
QString joinLayerId() const
ID of the joined layer - may be used to resolve reference to the joined layer.