Branch data Line data Source code
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 : : #include "qgsrelation_p.h" 24 : : #include "qgspolymorphicrelation.h" 25 : : #include "qgsrelationmanager.h" 26 : : 27 : 0 : QgsRelation::QgsRelation() 28 : 0 : : d( new QgsRelationPrivate() ) 29 : : { 30 : 0 : } 31 : : 32 : 0 : QgsRelation::QgsRelation( const QgsRelationContext &context ) 33 : 0 : : d( new QgsRelationPrivate() ) 34 : 0 : , mContext( context ) 35 : : { 36 : 0 : } 37 : : 38 : 0 : QgsRelation::~QgsRelation() = default; 39 : : 40 : 0 : QgsRelation::QgsRelation( const QgsRelation &other ) 41 : 0 : : d( other.d ) 42 : 0 : , mContext( other.mContext ) 43 : : { 44 : 0 : } 45 : : 46 : 0 : QgsRelation &QgsRelation::operator=( const QgsRelation &other ) 47 : : { 48 : 0 : d = other.d; 49 : 0 : mContext = other.mContext; 50 : 0 : return *this; 51 : : } 52 : : 53 : 0 : QgsRelation QgsRelation::createFromXml( const QDomNode &node, QgsReadWriteContext &context, const QgsRelationContext &relationContext ) 54 : : { 55 : 0 : QDomElement elem = node.toElement(); 56 : : 57 : 0 : if ( elem.tagName() != QLatin1String( "relation" ) ) 58 : : { 59 : 0 : QgsLogger::warning( QApplication::translate( "QgsRelation", "Cannot create relation. Unexpected tag '%1'" ).arg( elem.tagName() ) ); 60 : 0 : } 61 : : 62 : 0 : QgsRelation relation( relationContext ); 63 : : 64 : 0 : QString referencingLayerId = elem.attribute( QStringLiteral( "referencingLayer" ) ); 65 : 0 : QString referencedLayerId = elem.attribute( QStringLiteral( "referencedLayer" ) ); 66 : 0 : QString id = elem.attribute( QStringLiteral( "id" ) ); 67 : 0 : QString name = context.projectTranslator()->translate( QStringLiteral( "project:relations" ), elem.attribute( QStringLiteral( "name" ) ) ); 68 : 0 : QString strength = elem.attribute( QStringLiteral( "strength" ) ); 69 : : 70 : 0 : QMap<QString, QgsMapLayer *> mapLayers = relationContext.project()->mapLayers(); 71 : : 72 : 0 : QgsMapLayer *referencingLayer = mapLayers[referencingLayerId]; 73 : 0 : QgsMapLayer *referencedLayer = mapLayers[referencedLayerId]; 74 : : 75 : 0 : if ( !referencingLayer ) 76 : : { 77 : 0 : QgsLogger::warning( QApplication::translate( "QgsRelation", "Relation defined for layer '%1' which does not exist." ).arg( referencingLayerId ) ); 78 : 0 : } 79 : 0 : else if ( QgsMapLayerType::VectorLayer != referencingLayer->type() ) 80 : : { 81 : 0 : QgsLogger::warning( QApplication::translate( "QgsRelation", "Relation defined for layer '%1' which is not of type VectorLayer." ).arg( referencingLayerId ) ); 82 : 0 : } 83 : : 84 : 0 : if ( !referencedLayer ) 85 : : { 86 : 0 : QgsLogger::warning( QApplication::translate( "QgsRelation", "Relation defined for layer '%1' which does not exist." ).arg( referencedLayerId ) ); 87 : 0 : } 88 : 0 : else if ( QgsMapLayerType::VectorLayer != referencedLayer->type() ) 89 : : { 90 : 0 : QgsLogger::warning( QApplication::translate( "QgsRelation", "Relation defined for layer '%1' which is not of type VectorLayer." ).arg( referencedLayerId ) ); 91 : 0 : } 92 : : 93 : 0 : relation.d->mReferencingLayerId = referencingLayerId; 94 : 0 : relation.d->mReferencingLayer = qobject_cast<QgsVectorLayer *>( referencingLayer ); 95 : 0 : relation.d->mReferencedLayerId = referencedLayerId; 96 : 0 : relation.d->mReferencedLayer = qobject_cast<QgsVectorLayer *>( referencedLayer ); 97 : 0 : relation.d->mRelationId = id; 98 : 0 : relation.d->mRelationName = name; 99 : 0 : relation.d->mRelationStrength = qgsEnumKeyToValue<QgsRelation::RelationStrength>( strength, RelationStrength::Association ); 100 : : 101 : 0 : QDomNodeList references = elem.elementsByTagName( QStringLiteral( "fieldRef" ) ); 102 : 0 : for ( int i = 0; i < references.size(); ++i ) 103 : : { 104 : 0 : QDomElement refEl = references.at( i ).toElement(); 105 : : 106 : 0 : QString referencingField = refEl.attribute( QStringLiteral( "referencingField" ) ); 107 : 0 : QString referencedField = refEl.attribute( QStringLiteral( "referencedField" ) ); 108 : : 109 : 0 : relation.addFieldPair( referencingField, referencedField ); 110 : 0 : } 111 : : 112 : 0 : relation.updateRelationStatus(); 113 : : 114 : 0 : return relation; 115 : 0 : } 116 : : 117 : 0 : void QgsRelation::writeXml( QDomNode &node, QDomDocument &doc ) const 118 : : { 119 : 0 : QDomElement elem = doc.createElement( QStringLiteral( "relation" ) ); 120 : 0 : elem.setAttribute( QStringLiteral( "id" ), d->mRelationId ); 121 : 0 : elem.setAttribute( QStringLiteral( "name" ), d->mRelationName ); 122 : 0 : elem.setAttribute( QStringLiteral( "referencingLayer" ), d->mReferencingLayerId ); 123 : 0 : elem.setAttribute( QStringLiteral( "referencedLayer" ), d->mReferencedLayerId ); 124 : 0 : elem.setAttribute( QStringLiteral( "strength" ), qgsEnumValueToKey<RelationStrength>( d->mRelationStrength ) ); 125 : : 126 : 0 : for ( const FieldPair &pair : std::as_const( d->mFieldPairs ) ) 127 : : { 128 : 0 : QDomElement referenceElem = doc.createElement( QStringLiteral( "fieldRef" ) ); 129 : 0 : referenceElem.setAttribute( QStringLiteral( "referencingField" ), pair.first ); 130 : 0 : referenceElem.setAttribute( QStringLiteral( "referencedField" ), pair.second ); 131 : 0 : elem.appendChild( referenceElem ); 132 : 0 : } 133 : : 134 : 0 : node.appendChild( elem ); 135 : 0 : } 136 : : 137 : 0 : void QgsRelation::setId( const QString &id ) 138 : : { 139 : 0 : d.detach(); 140 : 0 : d->mRelationId = id; 141 : : 142 : 0 : updateRelationStatus(); 143 : 0 : } 144 : : 145 : 0 : void QgsRelation::setName( const QString &name ) 146 : : { 147 : 0 : d.detach(); 148 : 0 : d->mRelationName = name; 149 : 0 : } 150 : : 151 : : 152 : 0 : void QgsRelation::setStrength( RelationStrength strength ) 153 : : { 154 : 0 : d.detach(); 155 : 0 : d->mRelationStrength = strength; 156 : 0 : } 157 : : 158 : 0 : void QgsRelation::setReferencingLayer( const QString &id ) 159 : : { 160 : 0 : d.detach(); 161 : 0 : d->mReferencingLayerId = id; 162 : : 163 : 0 : updateRelationStatus(); 164 : 0 : } 165 : : 166 : 0 : void QgsRelation::setReferencedLayer( const QString &id ) 167 : : { 168 : 0 : d.detach(); 169 : 0 : d->mReferencedLayerId = id; 170 : : 171 : 0 : updateRelationStatus(); 172 : 0 : } 173 : : 174 : 0 : void QgsRelation::addFieldPair( const QString &referencingField, const QString &referencedField ) 175 : : { 176 : 0 : d.detach(); 177 : 0 : d->mFieldPairs << FieldPair( referencingField, referencedField ); 178 : 0 : updateRelationStatus(); 179 : 0 : } 180 : : 181 : 0 : void QgsRelation::addFieldPair( const FieldPair &fieldPair ) 182 : : { 183 : 0 : d.detach(); 184 : 0 : d->mFieldPairs << fieldPair; 185 : 0 : updateRelationStatus(); 186 : 0 : } 187 : : 188 : 0 : QgsFeatureIterator QgsRelation::getRelatedFeatures( const QgsFeature &feature ) const 189 : : { 190 : 0 : return referencingLayer()->getFeatures( getRelatedFeaturesRequest( feature ) ); 191 : 0 : } 192 : : 193 : 0 : QgsFeatureRequest QgsRelation::getRelatedFeaturesRequest( const QgsFeature &feature ) const 194 : : { 195 : 0 : QString filter = getRelatedFeaturesFilter( feature ); 196 : 0 : QgsDebugMsgLevel( QStringLiteral( "Filter conditions: '%1'" ).arg( filter ), 2 ); 197 : : 198 : 0 : QgsFeatureRequest myRequest; 199 : 0 : myRequest.setFilterExpression( filter ); 200 : 0 : return myRequest; 201 : 0 : } 202 : : 203 : 0 : QString QgsRelation::getRelatedFeaturesFilter( const QgsFeature &feature ) const 204 : : { 205 : 0 : QStringList conditions; 206 : : 207 : 0 : if ( ! d->mPolymorphicRelationId.isEmpty() ) 208 : : { 209 : 0 : QgsPolymorphicRelation polyRel = polymorphicRelation(); 210 : 0 : if ( polyRel.isValid() ) 211 : : { 212 : 0 : conditions << QgsExpression::createFieldEqualityExpression( polyRel.referencedLayerField(), polyRel.layerRepresentation( referencedLayer() ) ); 213 : 0 : } 214 : : else 215 : : { 216 : 0 : QgsDebugMsg( "The polymorphic relation is invalid" ); 217 : 0 : conditions << QStringLiteral( " FALSE " ); 218 : : } 219 : 0 : } 220 : : 221 : 0 : for ( const FieldPair &pair : std::as_const( d->mFieldPairs ) ) 222 : : { 223 : 0 : QVariant val( feature.attribute( pair.referencedField() ) ); 224 : 0 : conditions << QgsExpression::createFieldEqualityExpression( pair.referencingField(), val ); 225 : 0 : } 226 : : 227 : 0 : return conditions.join( QLatin1String( " AND " ) ); 228 : 0 : } 229 : : 230 : 0 : QgsFeatureRequest QgsRelation::getReferencedFeatureRequest( const QgsAttributes &attributes ) const 231 : : { 232 : 0 : QStringList conditions; 233 : : 234 : 0 : for ( const FieldPair &pair : std::as_const( d->mFieldPairs ) ) 235 : : { 236 : 0 : int referencingIdx = referencingLayer()->fields().lookupField( pair.referencingField() ); 237 : 0 : conditions << QgsExpression::createFieldEqualityExpression( pair.referencedField(), attributes.at( referencingIdx ) ); 238 : : } 239 : : 240 : 0 : QgsFeatureRequest myRequest; 241 : : 242 : 0 : QgsDebugMsgLevel( QStringLiteral( "Filter conditions: '%1'" ).arg( conditions.join( " AND " ) ), 2 ); 243 : : 244 : 0 : myRequest.setFilterExpression( conditions.join( QLatin1String( " AND " ) ) ); 245 : : 246 : 0 : return myRequest; 247 : 0 : } 248 : : 249 : 0 : QgsFeatureRequest QgsRelation::getReferencedFeatureRequest( const QgsFeature &feature ) const 250 : : { 251 : 0 : return getReferencedFeatureRequest( feature.attributes() ); 252 : 0 : } 253 : : 254 : 0 : QgsFeature QgsRelation::getReferencedFeature( const QgsFeature &feature ) const 255 : : { 256 : 0 : QgsFeatureRequest request = getReferencedFeatureRequest( feature ); 257 : : 258 : 0 : QgsFeature f; 259 : 0 : d->mReferencedLayer->getFeatures( request ).nextFeature( f ); 260 : 0 : return f; 261 : 0 : } 262 : : 263 : 0 : QString QgsRelation::name() const 264 : : { 265 : 0 : return d->mRelationName; 266 : : } 267 : : 268 : 0 : QgsRelation::RelationStrength QgsRelation::strength() const 269 : : { 270 : 0 : return d->mRelationStrength; 271 : : } 272 : : 273 : 0 : QString QgsRelation::id() const 274 : : { 275 : 0 : return d->mRelationId; 276 : : } 277 : : 278 : 0 : void QgsRelation::generateId() 279 : : { 280 : 0 : if ( !d->mFieldPairs.isEmpty() ) 281 : : { 282 : 0 : const QgsRelation::FieldPair fieldPair = d->mFieldPairs.at( 0 ); 283 : 0 : d->mRelationId = QStringLiteral( "%1_%2_%3_%4" ) 284 : 0 : .arg( referencingLayerId(), 285 : 0 : fieldPair.referencingField(), 286 : 0 : referencedLayerId(), 287 : 0 : fieldPair.referencedField() ); 288 : 0 : } 289 : 0 : updateRelationStatus(); 290 : 0 : } 291 : : 292 : 0 : QString QgsRelation::referencingLayerId() const 293 : : { 294 : 0 : return d->mReferencingLayerId; 295 : : } 296 : : 297 : 0 : QgsVectorLayer *QgsRelation::referencingLayer() const 298 : : { 299 : 0 : return d->mReferencingLayer; 300 : : } 301 : : 302 : 0 : QString QgsRelation::referencedLayerId() const 303 : : { 304 : 0 : return d->mReferencedLayerId; 305 : : } 306 : : 307 : 0 : QgsVectorLayer *QgsRelation::referencedLayer() const 308 : : { 309 : 0 : return d->mReferencedLayer; 310 : : } 311 : : 312 : 0 : QList<QgsRelation::FieldPair> QgsRelation::fieldPairs() const 313 : : { 314 : 0 : return d->mFieldPairs; 315 : : } 316 : : 317 : 0 : QgsAttributeList QgsRelation::referencedFields() const 318 : : { 319 : 0 : QgsAttributeList attrs; 320 : 0 : attrs.reserve( d->mFieldPairs.size() ); 321 : 0 : for ( const FieldPair &pair : std::as_const( d->mFieldPairs ) ) 322 : : { 323 : 0 : attrs << d->mReferencedLayer->fields().lookupField( pair.second ); 324 : : } 325 : 0 : return attrs; 326 : 0 : } 327 : : 328 : 0 : QgsAttributeList QgsRelation::referencingFields() const 329 : : { 330 : 0 : QgsAttributeList attrs; 331 : : 332 : 0 : for ( const FieldPair &pair : std::as_const( d->mFieldPairs ) ) 333 : : { 334 : 0 : attrs << d->mReferencingLayer->fields().lookupField( pair.first ); 335 : : } 336 : 0 : return attrs; 337 : : 338 : 0 : } 339 : : 340 : 0 : bool QgsRelation::isValid() const 341 : : { 342 : 0 : return d->mValid && !d->mReferencingLayer.isNull() && !d->mReferencedLayer.isNull() && d->mReferencingLayer.data()->isValid() && d->mReferencedLayer.data()->isValid(); 343 : : } 344 : : 345 : 0 : bool QgsRelation::hasEqualDefinition( const QgsRelation &other ) const 346 : : { 347 : 0 : return d->mReferencedLayerId == other.d->mReferencedLayerId && d->mReferencingLayerId == other.d->mReferencingLayerId && d->mFieldPairs == other.d->mFieldPairs; 348 : : } 349 : : 350 : 0 : QString QgsRelation::resolveReferencedField( const QString &referencingField ) const 351 : : { 352 : 0 : for ( const FieldPair &pair : std::as_const( d->mFieldPairs ) ) 353 : : { 354 : 0 : if ( pair.first == referencingField ) 355 : 0 : return pair.second; 356 : : } 357 : 0 : return QString(); 358 : 0 : } 359 : : 360 : 0 : QString QgsRelation::resolveReferencingField( const QString &referencedField ) const 361 : : { 362 : 0 : for ( const FieldPair &pair : std::as_const( d->mFieldPairs ) ) 363 : : { 364 : 0 : if ( pair.second == referencedField ) 365 : 0 : return pair.first; 366 : : } 367 : 0 : return QString(); 368 : 0 : } 369 : : 370 : 0 : void QgsRelation::updateRelationStatus() 371 : : { 372 : 0 : const QMap<QString, QgsMapLayer *> &mapLayers = mContext.project()->mapLayers(); 373 : : 374 : 0 : d->mReferencingLayer = qobject_cast<QgsVectorLayer *>( mapLayers[d->mReferencingLayerId] ); 375 : 0 : d->mReferencedLayer = qobject_cast<QgsVectorLayer *>( mapLayers[d->mReferencedLayerId] ); 376 : : 377 : 0 : d->mValid = true; 378 : : 379 : 0 : if ( d->mRelationId.isEmpty() ) 380 : : { 381 : 0 : QgsDebugMsg( QStringLiteral( "Invalid relation: no ID" ) ); 382 : 0 : d->mValid = false; 383 : 0 : } 384 : : else 385 : : { 386 : 0 : if ( !d->mReferencedLayer ) 387 : : { 388 : 0 : QgsDebugMsgLevel( QStringLiteral( "Invalid relation: referenced layer does not exist. ID: %1" ).arg( d->mReferencedLayerId ), 4 ); 389 : 0 : d->mValid = false; 390 : 0 : } 391 : 0 : else if ( !d->mReferencingLayer ) 392 : : { 393 : 0 : QgsDebugMsgLevel( QStringLiteral( "Invalid relation: referencing layer does not exist. ID: %2" ).arg( d->mReferencingLayerId ), 4 ); 394 : 0 : d->mValid = false; 395 : 0 : } 396 : : else 397 : : { 398 : 0 : if ( d->mFieldPairs.count() < 1 ) 399 : : { 400 : 0 : QgsDebugMsgLevel( QStringLiteral( "Invalid relation: no pair of field is specified." ), 4 ); 401 : 0 : d->mValid = false; 402 : 0 : } 403 : : 404 : 0 : for ( const FieldPair &pair : std::as_const( d->mFieldPairs ) ) 405 : : { 406 : 0 : if ( -1 == d->mReferencingLayer->fields().lookupField( pair.first ) ) 407 : : { 408 : 0 : QgsDebugMsg( QStringLiteral( "Invalid relation: field %1 does not exist in referencing layer %2" ).arg( pair.first, d->mReferencingLayer->name() ) ); 409 : 0 : d->mValid = false; 410 : 0 : break; 411 : : } 412 : 0 : else if ( -1 == d->mReferencedLayer->fields().lookupField( pair.second ) ) 413 : : { 414 : 0 : QgsDebugMsg( QStringLiteral( "Invalid relation: field %1 does not exist in referenced layer %2" ).arg( pair.second, d->mReferencedLayer->name() ) ); 415 : 0 : d->mValid = false; 416 : 0 : break; 417 : : } 418 : : } 419 : : } 420 : : 421 : : } 422 : 0 : } 423 : : 424 : 0 : void QgsRelation::setPolymorphicRelationId( const QString &polymorphicRelationId ) 425 : : { 426 : 0 : d.detach(); 427 : 0 : d->mPolymorphicRelationId = polymorphicRelationId; 428 : 0 : } 429 : : 430 : 0 : QString QgsRelation::polymorphicRelationId() const 431 : : { 432 : 0 : return d->mPolymorphicRelationId; 433 : : } 434 : : 435 : 0 : QgsPolymorphicRelation QgsRelation::polymorphicRelation() const 436 : : { 437 : 0 : if ( ! mContext.project() || ! mContext.project()->relationManager() ) 438 : 0 : return QgsPolymorphicRelation(); 439 : : 440 : 0 : return mContext.project()->relationManager()->polymorphicRelation( d->mPolymorphicRelationId ); 441 : 0 : } 442 : : 443 : 0 : QgsRelation::RelationType QgsRelation::type() const 444 : : { 445 : 0 : if ( d->mPolymorphicRelationId.isNull() ) 446 : 0 : return QgsRelation::Normal; 447 : : else 448 : 0 : return QgsRelation::Generated; 449 : 0 : }