QGIS API Documentation  3.2.0-Bonn (bc43194)
qgsrelation.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsrelation.cpp
3  --------------------------------------
4  Date : 29.4.2013
5  Copyright : (C) 2013 Matthias Kuhn
6  Email : matthias at opengis dot ch
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 #include "qgsrelation.h"
17 
18 #include "qgsapplication.h"
19 #include "qgsfeatureiterator.h"
20 #include "qgslogger.h"
21 #include "qgsproject.h"
22 #include "qgsvectorlayer.h"
23 
24 QgsRelation QgsRelation::createFromXml( const QDomNode &node )
25 {
26  QDomElement elem = node.toElement();
27 
28  if ( elem.tagName() != QLatin1String( "relation" ) )
29  {
30  QgsLogger::warning( QApplication::translate( "QgsRelation", "Cannot create relation. Unexpected tag '%1'" ).arg( elem.tagName() ) );
31  }
32 
33  QgsRelation relation;
34 
35  QString referencingLayerId = elem.attribute( QStringLiteral( "referencingLayer" ) );
36  QString referencedLayerId = elem.attribute( QStringLiteral( "referencedLayer" ) );
37  QString id = elem.attribute( QStringLiteral( "id" ) );
38  QString name = elem.attribute( QStringLiteral( "name" ) );
39  QString strength = elem.attribute( QStringLiteral( "strength" ) );
40 
41  const QMap<QString, QgsMapLayer *> &mapLayers = QgsProject::instance()->mapLayers();
42 
45 
46  if ( !referencingLayer )
47  {
48  QgsLogger::warning( QApplication::translate( "QgsRelation", "Relation defined for layer '%1' which does not exist." ).arg( referencingLayerId ) );
49  }
50  else if ( QgsMapLayer::VectorLayer != referencingLayer->type() )
51  {
52  QgsLogger::warning( QApplication::translate( "QgsRelation", "Relation defined for layer '%1' which is not of type VectorLayer." ).arg( referencingLayerId ) );
53  }
54 
55  if ( !referencedLayer )
56  {
57  QgsLogger::warning( QApplication::translate( "QgsRelation", "Relation defined for layer '%1' which does not exist." ).arg( referencedLayerId ) );
58  }
59  else if ( QgsMapLayer::VectorLayer != referencedLayer->type() )
60  {
61  QgsLogger::warning( QApplication::translate( "QgsRelation", "Relation defined for layer '%1' which is not of type VectorLayer." ).arg( referencedLayerId ) );
62  }
63 
64  relation.mReferencingLayerId = referencingLayerId;
65  relation.mReferencingLayer = qobject_cast<QgsVectorLayer *>( referencingLayer );
66  relation.mReferencedLayerId = referencedLayerId;
67  relation.mReferencedLayer = qobject_cast<QgsVectorLayer *>( referencedLayer );
68  relation.mRelationId = id;
69  relation.mRelationName = name;
70  if ( strength == "Composition" )
71  {
72  relation.mRelationStrength = RelationStrength::Composition;
73  }
74  else
75  {
76  relation.mRelationStrength = RelationStrength::Association;
77  }
78 
79  QDomNodeList references = elem.elementsByTagName( QStringLiteral( "fieldRef" ) );
80  for ( int i = 0; i < references.size(); ++i )
81  {
82  QDomElement refEl = references.at( i ).toElement();
83 
84  QString referencingField = refEl.attribute( QStringLiteral( "referencingField" ) );
85  QString referencedField = refEl.attribute( QStringLiteral( "referencedField" ) );
86 
87  relation.addFieldPair( referencingField, referencedField );
88  }
89 
90  relation.updateRelationStatus();
91 
92  return relation;
93 }
94 
95 void QgsRelation::writeXml( QDomNode &node, QDomDocument &doc ) const
96 {
97  QDomElement elem = doc.createElement( QStringLiteral( "relation" ) );
98  elem.setAttribute( QStringLiteral( "id" ), mRelationId );
99  elem.setAttribute( QStringLiteral( "name" ), mRelationName );
100  elem.setAttribute( QStringLiteral( "referencingLayer" ), mReferencingLayerId );
101  elem.setAttribute( QStringLiteral( "referencedLayer" ), mReferencedLayerId );
102  if ( mRelationStrength == RelationStrength::Composition )
103  {
104  elem.setAttribute( QStringLiteral( "strength" ), QStringLiteral( "Composition" ) );
105  }
106  else
107  {
108  elem.setAttribute( QStringLiteral( "strength" ), QStringLiteral( "Association" ) );
109  }
110 
111  Q_FOREACH ( const FieldPair &fields, mFieldPairs )
112  {
113  QDomElement referenceElem = doc.createElement( QStringLiteral( "fieldRef" ) );
114  referenceElem.setAttribute( QStringLiteral( "referencingField" ), fields.first );
115  referenceElem.setAttribute( QStringLiteral( "referencedField" ), fields.second );
116  elem.appendChild( referenceElem );
117  }
118 
119  node.appendChild( elem );
120 }
121 
122 void QgsRelation::setId( const QString &id )
123 {
124  mRelationId = id;
125 
126  updateRelationStatus();
127 }
128 
129 void QgsRelation::setName( const QString &name )
130 {
131  mRelationName = name;
132 }
133 
134 
136 {
137  mRelationStrength = strength;
138 }
139 
140 void QgsRelation::setReferencingLayer( const QString &id )
141 {
142  mReferencingLayerId = id;
143 
144  updateRelationStatus();
145 }
146 
147 void QgsRelation::setReferencedLayer( const QString &id )
148 {
149  mReferencedLayerId = id;
150 
151  updateRelationStatus();
152 }
153 
154 void QgsRelation::addFieldPair( const QString &referencingField, const QString &referencedField )
155 {
156  mFieldPairs << FieldPair( referencingField, referencedField );
157  updateRelationStatus();
158 }
159 
160 void QgsRelation::addFieldPair( const FieldPair &fieldPair )
161 {
162  mFieldPairs << fieldPair;
163  updateRelationStatus();
164 }
165 
167 {
169 }
170 
172 {
173  QString filter = getRelatedFeaturesFilter( feature );
174  QgsDebugMsg( QString( "Filter conditions: '%1'" ).arg( filter ) );
175 
176  QgsFeatureRequest myRequest;
177  myRequest.setFilterExpression( filter );
178  return myRequest;
179 }
180 
181 QString QgsRelation::getRelatedFeaturesFilter( const QgsFeature &feature ) const
182 {
183  QStringList conditions;
184 
185  Q_FOREACH ( const QgsRelation::FieldPair &fieldPair, mFieldPairs )
186  {
187  QVariant val( feature.attribute( fieldPair.referencedField() ) );
188  conditions << QgsExpression::createFieldEqualityExpression( fieldPair.referencingField(), val );
189  }
190 
191  return conditions.join( QStringLiteral( " AND " ) );
192 }
193 
195 {
196  QStringList conditions;
197 
198  Q_FOREACH ( const QgsRelation::FieldPair &fieldPair, mFieldPairs )
199  {
200  int referencingIdx = referencingLayer()->fields().indexFromName( fieldPair.referencingField() );
201  conditions << QgsExpression::createFieldEqualityExpression( fieldPair.referencedField(), attributes.at( referencingIdx ) );
202  }
203 
204  QgsFeatureRequest myRequest;
205 
206  QgsDebugMsg( QString( "Filter conditions: '%1'" ).arg( conditions.join( " AND " ) ) );
207 
208  myRequest.setFilterExpression( conditions.join( QStringLiteral( " AND " ) ) );
209 
210  return myRequest;
211 }
212 
214 {
215  return getReferencedFeatureRequest( feature.attributes() );
216 }
217 
219 {
220  QgsFeatureRequest request = getReferencedFeatureRequest( feature );
221 
222  QgsFeature f;
223  mReferencedLayer->getFeatures( request ).nextFeature( f );
224  return f;
225 }
226 
227 QString QgsRelation::name() const
228 {
229  return mRelationName;
230 }
231 
233 {
234  return mRelationStrength;
235 }
236 
237 QString QgsRelation::id() const
238 {
239  return mRelationId;
240 }
241 
243 {
244  mRelationId = QStringLiteral( "%1_%2_%3_%4" )
245  .arg( referencingLayerId(),
246  mFieldPairs.at( 0 ).referencingField(),
248  mFieldPairs.at( 0 ).referencedField() );
249  updateRelationStatus();
250 }
251 
253 {
254  return mReferencingLayerId;
255 }
256 
258 {
259  return mReferencingLayer;
260 }
261 
263 {
264  return mReferencedLayerId;
265 }
266 
268 {
269  return mReferencedLayer;
270 }
271 
272 QList<QgsRelation::FieldPair> QgsRelation::fieldPairs() const
273 {
274  return mFieldPairs;
275 }
276 
278 {
279  QgsAttributeList attrs;
280 
281  Q_FOREACH ( const FieldPair &pair, mFieldPairs )
282  {
283  attrs << mReferencedLayer->fields().lookupField( pair.second );
284  }
285  return attrs;
286 }
287 
289 {
290  QgsAttributeList attrs;
291 
292  Q_FOREACH ( const FieldPair &pair, mFieldPairs )
293  {
294  attrs << mReferencingLayer->fields().lookupField( pair.first );
295  }
296  return attrs;
297 
298 }
299 
300 bool QgsRelation::isValid() const
301 {
302  return mValid;
303 }
304 
306 {
307  return mReferencedLayerId == other.mReferencedLayerId && mReferencingLayerId == other.mReferencingLayerId && mFieldPairs == other.mFieldPairs;
308 }
309 
310 QString QgsRelation::resolveReferencedField( const QString &referencingField ) const
311 {
312  Q_FOREACH ( const FieldPair &pair, mFieldPairs )
313  {
314  if ( pair.first == referencingField )
315  return pair.second;
316  }
317  return QString();
318 }
319 
320 QString QgsRelation::resolveReferencingField( const QString &referencedField ) const
321 {
322  Q_FOREACH ( const FieldPair &pair, mFieldPairs )
323  {
324  if ( pair.second == referencedField )
325  return pair.first;
326  }
327  return QString();
328 }
329 
330 void QgsRelation::updateRelationStatus()
331 {
332  const QMap<QString, QgsMapLayer *> &mapLayers = QgsProject::instance()->mapLayers();
333 
334  mReferencingLayer = qobject_cast<QgsVectorLayer *>( mapLayers[mReferencingLayerId] );
335  mReferencedLayer = qobject_cast<QgsVectorLayer *>( mapLayers[mReferencedLayerId] );
336 
337  mValid = true;
338 
339  if ( mRelationId.isEmpty() )
340  {
341  QgsDebugMsg( "Invalid relation: no ID" );
342  mValid = false;
343  }
344  else
345  {
346  if ( !mReferencedLayer )
347  {
348  QgsDebugMsgLevel( QStringLiteral( "Invalid relation: referenced layer does not exist. ID: %1" ).arg( mReferencedLayerId ), 4 );
349  mValid = false;
350  }
351  else if ( !mReferencingLayer )
352  {
353  QgsDebugMsgLevel( QStringLiteral( "Invalid relation: referencing layer does not exist. ID: %2" ).arg( mReferencingLayerId ), 4 );
354  mValid = false;
355  }
356  else
357  {
358  if ( mFieldPairs.count() < 1 )
359  {
360  QgsDebugMsgLevel( "Invalid relation: no pair of field is specified.", 4 );
361  mValid = false;
362  }
363 
364  Q_FOREACH ( const FieldPair &fieldPair, mFieldPairs )
365  {
366  if ( -1 == mReferencingLayer->fields().lookupField( fieldPair.first ) )
367  {
368  QgsDebugMsg( QStringLiteral( "Invalid relation: field %1 does not exist in referencing layer %2" ).arg( fieldPair.first, mReferencingLayer->name() ) );
369  mValid = false;
370  break;
371  }
372  else if ( -1 == mReferencedLayer->fields().lookupField( fieldPair.second ) )
373  {
374  QgsDebugMsg( QStringLiteral( "Invalid relation: field %1 does not exist in referencedg layer %2" ).arg( fieldPair.second, mReferencedLayer->name() ) );
375  mValid = false;
376  break;
377  }
378  }
379  }
380 
381  }
382 }
int lookupField(const QString &fieldName) const
Look up field&#39;s index from the field name.
Definition: qgsfields.cpp:299
Wrapper for iterator of features from vector data provider or vector layer.
bool isValid() const
Returns the validity of this relation.
Base class for all map layer types.
Definition: qgsmaplayer.h:61
void generateId()
Generate a (project-wide) unique id for this relation.
QgsAttributeList referencingFields() const
Returns a list of attributes used to form the referencing fields (foreign key) on the referencing (ch...
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
static void warning(const QString &msg)
Goes to qWarning.
Definition: qgslogger.cpp:121
void setReferencingLayer(const QString &id)
Set the referencing (child) layer id.
void addFieldPair(const QString &referencingField, const QString &referencedField)
Add a field pairs which is part of this relation The first element of each pair are the field names o...
void setName(const QString &name)
Set a name for this relation.
QMap< QString, QgsMapLayer * > mapLayers() const
Returns a map of all registered layers by layer ID.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:62
QgsMapLayer::LayerType type() const
Returns the type of the layer.
QString id() const
A (project-wide) unique id for this relation.
void setId(const QString &id)
Set an id for this relation.
void setStrength(RelationStrength strength)
Set a strength for this relation.
QgsAttributeList referencedFields() const
Returns a list of attributes used to form the referenced fields (most likely primary key) on the refe...
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
int indexFromName(const QString &fieldName) const
Gets the field index from the field name.
Definition: qgsfields.cpp:184
RelationStrength strength() const
Returns the relation strength as a string.
QgsFeatureRequest getReferencedFeatureRequest(const QgsAttributes &attributes) const
Creates a request to return the feature on the referenced (parent) layer which is referenced by the p...
Defines a relation between matching fields of the two involved tables of a relation.
Definition: qgsrelation.h:72
QgsFields fields() const override
Returns the list of fields of this layer.
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
Q_INVOKABLE QString resolveReferencedField(const QString &referencingField) const
Gets the referenced field counterpart given a referencing field.
This class wraps a request for features to a vector layer (or directly its vector data provider)...
QgsVectorLayer * referencedLayer() const
Access the referenced (parent) layer.
static QgsRelation createFromXml(const QDomNode &node)
Creates a relation from an XML structure.
Definition: qgsrelation.cpp:24
QgsFeatureRequest getRelatedFeaturesRequest(const QgsFeature &feature) const
Creates a request to return all the features on the referencing (child) layer which have a foreign ke...
Q_INVOKABLE QString resolveReferencingField(const QString &referencedField) const
Gets the referencing field counterpart given a referenced field.
bool hasEqualDefinition(const QgsRelation &other) const
Compares the two QgsRelation, ignoring the name and the ID.
static QString createFieldEqualityExpression(const QString &fieldName, const QVariant &value)
Create an expression allowing to evaluate if a field is equal to a value.
void setReferencedLayer(const QString &id)
Set the referenced (parent) layer id.
QList< QgsRelation::FieldPair > fieldPairs() const
Returns the field pairs which form this relation The first element of each pair are the field names o...
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const override
Query the layer for features specified in request.
QgsFeatureIterator getRelatedFeatures(const QgsFeature &feature) const
Creates an iterator which returns all the features on the referencing (child) layer which have a fore...
QString referencingLayerId() const
Access the referencing (child) layer&#39;s id This is the layer which has the field(s) which point to ano...
RelationStrength
enum for the relation strength Association, Composition
Definition: qgsrelation.h:55
QString getRelatedFeaturesFilter(const QgsFeature &feature) const
Returns a filter expression which returns all the features on the referencing (child) layer which hav...
QgsVectorLayer * referencingLayer() const
Access the referencing (child) layer This is the layer which has the field(s) which point to another ...
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:391
QString referencedField() const
Gets the name of the referenced (parent) field.
Definition: qgsrelation.h:85
QString name
Definition: qgsmaplayer.h:65
QList< int > QgsAttributeList
Definition: qgsfield.h:27
bool nextFeature(QgsFeature &f)
QgsFeature getReferencedFeature(const QgsFeature &feature) const
Creates a request to return the feature on the referenced (parent) layer which is referenced by the p...
A vector of attributes.
Definition: qgsattributes.h:58
Represents a vector layer which manages a vector based data sets.
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
Definition: qgsfeature.cpp:255
QString referencedLayerId() const
Access the referenced (parent) layer&#39;s id.
QString referencingField() const
Gets the name of the referencing (child) field.
Definition: qgsrelation.h:83
void writeXml(QDomNode &node, QDomDocument &doc) const
Writes a relation to an XML structure.
Definition: qgsrelation.cpp:95
QgsAttributes attributes
Definition: qgsfeature.h:72
QString name() const
Returns a human readable name for this relation.