Branch data Line data Source code
1 : : /*************************************************************************** 2 : : qgspolymorphicrelation.h 3 : : -------------------------------------- 4 : : Date : December 2020 5 : : Copyright : (C) 2020 Ivan Ivanov 6 : : Email : ivan 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 "qgspolymorphicrelation.h" 17 : : 18 : : #include "qgsapplication.h" 19 : : #include "qgsfeatureiterator.h" 20 : : #include "qgslogger.h" 21 : : #include "qgsproject.h" 22 : : #include "qgsvectorlayer.h" 23 : : #include "qgspolymorphicrelation_p.h" 24 : : #include "qgsexpressioncontextutils.h" 25 : : 26 : 0 : QgsPolymorphicRelation::QgsPolymorphicRelation() 27 : 0 : : d( new QgsPolymorphicRelationPrivate() ) 28 : : { 29 : 0 : } 30 : : 31 : 0 : QgsPolymorphicRelation::QgsPolymorphicRelation( const QgsRelationContext &context ) 32 : 0 : : d( new QgsPolymorphicRelationPrivate() ) 33 : 0 : , mContext( context ) 34 : : { 35 : 0 : } 36 : : 37 : 0 : QgsPolymorphicRelation::~QgsPolymorphicRelation() = default; 38 : : 39 : 0 : QgsPolymorphicRelation::QgsPolymorphicRelation( const QgsPolymorphicRelation &other ) 40 : 0 : : d( other.d ) 41 : 0 : , mContext( other.mContext ) 42 : : { 43 : 0 : } 44 : : 45 : 0 : QgsPolymorphicRelation &QgsPolymorphicRelation::operator=( const QgsPolymorphicRelation &other ) 46 : : { 47 : 0 : d = other.d; 48 : 0 : mContext = other.mContext; 49 : 0 : return *this; 50 : : } 51 : : 52 : 0 : QgsPolymorphicRelation QgsPolymorphicRelation::createFromXml( const QDomNode &node, QgsReadWriteContext &context, const QgsRelationContext &relationContext ) 53 : : { 54 : 0 : Q_UNUSED( context ); 55 : 0 : QDomElement elem = node.toElement(); 56 : : 57 : 0 : if ( elem.tagName() != QLatin1String( "relation" ) ) 58 : : { 59 : 0 : QgsLogger::warning( QApplication::translate( "QgsPolymorphicRelation", "Cannot create relation. Unexpected tag '%1'" ).arg( elem.tagName() ) ); 60 : 0 : } 61 : : 62 : 0 : QgsPolymorphicRelation relation( relationContext ); 63 : : 64 : 0 : QString referencingLayerId = elem.attribute( QStringLiteral( "referencingLayer" ) ); 65 : 0 : QString referencedLayerField = elem.attribute( QStringLiteral( "referencedLayerField" ) ); 66 : 0 : QString referencedLayerExpression = elem.attribute( QStringLiteral( "referencedLayerExpression" ) ); 67 : 0 : QString id = elem.attribute( QStringLiteral( "id" ) ); 68 : 0 : QString name = elem.attribute( QStringLiteral( "name" ) ); 69 : 0 : QString relationStrength = elem.attribute( QStringLiteral( "relationStrength" ) ); 70 : 0 : QStringList referencedLayerIds = elem.attribute( QStringLiteral( "referencedLayerIds" ) ).split( "," ); 71 : : 72 : 0 : QMap<QString, QgsMapLayer *> mapLayers = relationContext.project()->mapLayers(); 73 : : 74 : 0 : relation.d->mReferencingLayerId = referencingLayerId; 75 : 0 : relation.d->mReferencingLayer = qobject_cast<QgsVectorLayer *>( mapLayers[referencingLayerId] ); 76 : 0 : relation.d->mReferencedLayerField = referencedLayerField; 77 : 0 : relation.d->mReferencedLayerExpression = referencedLayerExpression; 78 : 0 : relation.d->mReferencedLayerIds = referencedLayerIds; 79 : 0 : relation.d->mRelationId = id; 80 : 0 : relation.d->mRelationName = name; 81 : 0 : relation.d->mRelationStrength = qgsEnumKeyToValue<QgsRelation::RelationStrength>( relationStrength, QgsRelation::RelationStrength::Association ); 82 : : 83 : 0 : QDomNodeList references = elem.elementsByTagName( QStringLiteral( "fieldRef" ) ); 84 : 0 : for ( int i = 0; i < references.size(); ++i ) 85 : : { 86 : 0 : QDomElement refEl = references.at( i ).toElement(); 87 : : 88 : 0 : QString referencingField = refEl.attribute( QStringLiteral( "referencingField" ) ); 89 : 0 : QString referencedField = refEl.attribute( QStringLiteral( "referencedField" ) ); 90 : : 91 : 0 : relation.addFieldPair( referencingField, referencedField ); 92 : 0 : } 93 : : 94 : 0 : relation.updateRelationStatus(); 95 : : 96 : 0 : return relation; 97 : 0 : } 98 : : 99 : 0 : void QgsPolymorphicRelation::writeXml( QDomNode &node, QDomDocument &doc ) const 100 : : { 101 : 0 : QDomElement elem = doc.createElement( QStringLiteral( "relation" ) ); 102 : 0 : elem.setAttribute( QStringLiteral( "id" ), d->mRelationId ); 103 : 0 : elem.setAttribute( QStringLiteral( "name" ), d->mRelationName ); 104 : 0 : elem.setAttribute( QStringLiteral( "referencingLayer" ), d->mReferencingLayerId ); 105 : 0 : elem.setAttribute( QStringLiteral( "referencedLayerField" ), d->mReferencedLayerField ); 106 : 0 : elem.setAttribute( QStringLiteral( "referencedLayerExpression" ), d->mReferencedLayerExpression ); 107 : 0 : elem.setAttribute( QStringLiteral( "referencedLayerIds" ), d->mReferencedLayerIds.join( "," ) ); 108 : 0 : elem.setAttribute( QStringLiteral( "relationStrength" ), qgsEnumValueToKey<QgsRelation::RelationStrength>( d->mRelationStrength ) ); 109 : : 110 : : // note that a layer id can store a comma in theory. Luckyly, this is not easy to achieve, e.g. you need to modify the .qgs file manually 111 : 0 : for ( const QString &layerId : std::as_const( d->mReferencedLayerIds ) ) 112 : : Q_ASSERT( ! layerId.contains( "," ) ); 113 : : 114 : 0 : for ( const QgsRelation::FieldPair &pair : std::as_const( d->mFieldPairs ) ) 115 : : { 116 : 0 : QDomElement referenceElem = doc.createElement( QStringLiteral( "fieldRef" ) ); 117 : 0 : referenceElem.setAttribute( QStringLiteral( "referencingField" ), pair.first ); 118 : 0 : referenceElem.setAttribute( QStringLiteral( "referencedField" ), pair.second ); 119 : 0 : elem.appendChild( referenceElem ); 120 : 0 : } 121 : : 122 : 0 : node.appendChild( elem ); 123 : 0 : } 124 : : 125 : 0 : void QgsPolymorphicRelation::setId( const QString &id ) 126 : : { 127 : 0 : if ( d->mRelationId == id ) 128 : 0 : return; 129 : : 130 : 0 : d.detach(); 131 : 0 : d->mRelationId = id; 132 : : 133 : 0 : updateRelationStatus(); 134 : 0 : } 135 : : 136 : 0 : void QgsPolymorphicRelation::setReferencingLayer( const QString &id ) 137 : : { 138 : 0 : d.detach(); 139 : 0 : d->mReferencingLayerId = id; 140 : : 141 : 0 : updateRelationStatus(); 142 : 0 : } 143 : : 144 : 0 : void QgsPolymorphicRelation::addFieldPair( const QString &referencingField, const QString &referencedField ) 145 : : { 146 : 0 : d.detach(); 147 : 0 : d->mFieldPairs << QgsRelation::FieldPair( referencingField, referencedField ); 148 : 0 : updateRelationStatus(); 149 : 0 : } 150 : : 151 : 0 : void QgsPolymorphicRelation::addFieldPair( const QgsRelation::FieldPair &fieldPair ) 152 : : { 153 : 0 : d.detach(); 154 : 0 : d->mFieldPairs << fieldPair; 155 : 0 : updateRelationStatus(); 156 : 0 : } 157 : : 158 : 0 : QString QgsPolymorphicRelation::id() const 159 : : { 160 : 0 : return d->mRelationId; 161 : : } 162 : : 163 : 0 : void QgsPolymorphicRelation::generateId() 164 : : { 165 : 0 : d->mRelationId = QStringLiteral( "%1_%2_%3_%4" ) 166 : 0 : .arg( referencingLayerId(), 167 : 0 : d->mFieldPairs.at( 0 ).referencingField(), 168 : 0 : referencedLayerField(), 169 : 0 : d->mFieldPairs.at( 0 ).referencedField() ); 170 : 0 : updateRelationStatus(); 171 : 0 : } 172 : : 173 : 0 : QString QgsPolymorphicRelation::referencingLayerId() const 174 : : { 175 : 0 : return d->mReferencingLayerId; 176 : : } 177 : : 178 : 0 : QgsVectorLayer *QgsPolymorphicRelation::referencingLayer() const 179 : : { 180 : 0 : return d->mReferencingLayer; 181 : : } 182 : : 183 : 0 : QList<QgsRelation::FieldPair> QgsPolymorphicRelation::fieldPairs() const 184 : : { 185 : 0 : return d->mFieldPairs; 186 : : } 187 : : 188 : 0 : QgsAttributeList QgsPolymorphicRelation::referencedFields( const QString &layerId ) const 189 : : { 190 : 0 : QgsAttributeList attrs; 191 : : 192 : 0 : if ( d->mReferencedLayerIds.contains( layerId ) ) 193 : : { 194 : 0 : QgsVectorLayer *vl = static_cast<QgsVectorLayer *>( QgsProject::instance()->mapLayer( layerId ) ); 195 : : 196 : 0 : if ( vl && vl->isValid() ) 197 : : { 198 : 0 : for ( const QgsRelation::FieldPair &pair : std::as_const( d->mFieldPairs ) ) 199 : : { 200 : 0 : attrs << vl->fields().lookupField( pair.second ); 201 : : } 202 : 0 : } 203 : 0 : } 204 : : 205 : 0 : return attrs; 206 : 0 : } 207 : : 208 : 0 : QgsAttributeList QgsPolymorphicRelation::referencingFields() const 209 : : { 210 : 0 : QgsAttributeList attrs; 211 : : 212 : 0 : for ( const QgsRelation::FieldPair &pair : std::as_const( d->mFieldPairs ) ) 213 : : { 214 : 0 : attrs << d->mReferencingLayer->fields().lookupField( pair.first ); 215 : : } 216 : 0 : return attrs; 217 : : 218 : 0 : } 219 : : 220 : 0 : bool QgsPolymorphicRelation::isValid() const 221 : : { 222 : 0 : return d->mValid && !d->mReferencingLayer.isNull() && !d->mReferencedLayerField.isNull() && d->mReferencingLayer.data()->isValid() && !d->mReferencedLayerExpression.isNull(); 223 : : } 224 : : 225 : 0 : bool QgsPolymorphicRelation::hasEqualDefinition( const QgsPolymorphicRelation &other ) const 226 : : { 227 : 0 : return d->mReferencedLayerField == other.d->mReferencedLayerField 228 : 0 : && d->mReferencedLayerExpression == other.d->mReferencedLayerExpression 229 : 0 : && d->mReferencingLayerId == other.d->mReferencingLayerId 230 : 0 : && d->mFieldPairs == other.d->mFieldPairs; 231 : : } 232 : : 233 : 0 : void QgsPolymorphicRelation::updateRelationStatus() 234 : : { 235 : 0 : const QMap<QString, QgsMapLayer *> &mapLayers = mContext.project()->mapLayers(); 236 : : 237 : 0 : d->mValid = true; 238 : 0 : d->mReferencingLayer = mapLayers.contains( d->mReferencingLayerId ) 239 : 0 : ? qobject_cast<QgsVectorLayer *>( mapLayers[d->mReferencingLayerId] ) 240 : : : nullptr; 241 : 0 : d->mReferencedLayersMap.clear(); 242 : : 243 : 0 : if ( d->mRelationId.isEmpty() ) 244 : : { 245 : 0 : QgsDebugMsg( QStringLiteral( "Invalid relation: no ID" ) ); 246 : 0 : d->mValid = false; 247 : 0 : return; 248 : : } 249 : : 250 : 0 : if ( !d->mReferencingLayer ) 251 : : { 252 : 0 : QgsDebugMsgLevel( QStringLiteral( "Invalid relation: referencing layer does not exist. ID: %1" ).arg( d->mReferencingLayerId ), 4 ); 253 : 0 : d->mValid = false; 254 : 0 : return; 255 : : } 256 : : 257 : 0 : if ( d->mReferencingLayer->fields().lookupField( d->mReferencedLayerField ) == -1 ) 258 : : { 259 : 0 : QgsDebugMsgLevel( QStringLiteral( "Invalid relation: referenced layer field \"%1\" does not exist in layer with ID: %2" ).arg( d->mReferencedLayerField, d->mReferencingLayerId ), 4 ); 260 : 0 : d->mValid = false; 261 : 0 : return; 262 : : } 263 : : 264 : 0 : if ( d->mReferencedLayerExpression.trimmed().isNull() ) 265 : : { 266 : 0 : QgsDebugMsgLevel( QStringLiteral( "Invalid relation: referenced layer expression \"%1\" is missing" ).arg( d->mReferencedLayerExpression ), 4 ); 267 : 0 : d->mValid = false; 268 : 0 : return; 269 : : } 270 : : 271 : 0 : const QStringList referencedLayerIds = d->mReferencedLayerIds; 272 : 0 : for ( const QString &referencedLayerId : referencedLayerIds ) 273 : : { 274 : 0 : d->mReferencedLayersMap.insert( referencedLayerId, qobject_cast<QgsVectorLayer *>( mapLayers[referencedLayerId] ) ); 275 : : 276 : 0 : if ( !d->mReferencedLayersMap[referencedLayerId] || !d->mReferencedLayersMap[referencedLayerId]->isValid() ) 277 : : { 278 : 0 : QgsDebugMsgLevel( QStringLiteral( "Invalid relation: referenced layer does not exist. ID: %1" ).arg( d->mReferencedLayersMap[referencedLayerId]->id() ), 4 ); 279 : 0 : d->mValid = false; 280 : 0 : return; 281 : : } 282 : : } 283 : : 284 : 0 : if ( d->mFieldPairs.count() == 0 ) 285 : : { 286 : 0 : QgsDebugMsgLevel( QStringLiteral( "Invalid relation: no pair of field is specified." ), 4 ); 287 : 0 : d->mValid = false; 288 : 0 : return; 289 : : } 290 : : 291 : 0 : for ( const QgsRelation::FieldPair &pair : std::as_const( d->mFieldPairs ) ) 292 : : { 293 : 0 : if ( d->mReferencingLayer->fields().lookupField( pair.first ) == -1 ) 294 : : { 295 : 0 : QgsDebugMsg( QStringLiteral( "Invalid relation: field %1 does not exist in referencing layer %2" ).arg( pair.first, d->mReferencingLayer->name() ) ); 296 : 0 : d->mValid = false; 297 : 0 : return; 298 : : } 299 : : 300 : 0 : for ( const QString &referencedLayerId : referencedLayerIds ) 301 : : { 302 : 0 : if ( d->mReferencedLayersMap[referencedLayerId]->fields().lookupField( pair.second ) == -1 ) 303 : : { 304 : 0 : QgsDebugMsg( QStringLiteral( "Invalid relation: field %1 does not exist in referenced layer %2" ).arg( pair.second, d->mReferencedLayersMap[referencedLayerId]->name() ) ); 305 : 0 : d->mValid = false; 306 : 0 : return; 307 : : } 308 : : } 309 : : } 310 : 0 : } 311 : : 312 : 0 : void QgsPolymorphicRelation::setName( const QString &name ) 313 : : { 314 : 0 : if ( d->mRelationName == name && !name.isEmpty() ) 315 : 0 : return; 316 : : 317 : 0 : d.detach(); 318 : 0 : d->mRelationName = name; 319 : 0 : updateRelationStatus(); 320 : 0 : } 321 : : 322 : 0 : QString QgsPolymorphicRelation::name() const 323 : : { 324 : 0 : if ( d->mRelationName.isEmpty() ) 325 : 0 : return QObject::tr( "Polymorphic relations for \"%1\"" ).arg( d->mReferencingLayer ? d->mReferencingLayer->name() : QStringLiteral( "<NO LAYER>" ) ); 326 : : 327 : 0 : return d->mRelationName; 328 : 0 : } 329 : : 330 : 0 : void QgsPolymorphicRelation::setReferencedLayerField( const QString &referencedLayerField ) 331 : : { 332 : 0 : d.detach(); 333 : 0 : d->mReferencedLayerField = referencedLayerField; 334 : 0 : updateRelationStatus(); 335 : 0 : } 336 : : 337 : 0 : QString QgsPolymorphicRelation::referencedLayerField() const 338 : : { 339 : 0 : return d->mReferencedLayerField; 340 : : } 341 : : 342 : 0 : void QgsPolymorphicRelation::setReferencedLayerExpression( const QString &referencedLayerExpression ) 343 : : { 344 : 0 : d.detach(); 345 : 0 : d->mReferencedLayerExpression = referencedLayerExpression; 346 : 0 : updateRelationStatus(); 347 : 0 : } 348 : : 349 : 0 : QString QgsPolymorphicRelation::referencedLayerExpression() const 350 : : { 351 : 0 : return d->mReferencedLayerExpression; 352 : : } 353 : : 354 : 0 : void QgsPolymorphicRelation::setReferencedLayerIds( const QStringList &referencedLayerIds ) 355 : : { 356 : 0 : d.detach(); 357 : 0 : d->mReferencedLayerIds = referencedLayerIds; 358 : 0 : updateRelationStatus(); 359 : 0 : } 360 : : 361 : 0 : QStringList QgsPolymorphicRelation::referencedLayerIds() const 362 : : { 363 : 0 : return d->mReferencedLayerIds; 364 : : } 365 : : 366 : 0 : QgsRelation::RelationStrength QgsPolymorphicRelation::strength() const 367 : : { 368 : 0 : return d->mRelationStrength; 369 : : } 370 : : 371 : 0 : void QgsPolymorphicRelation::setRelationStrength( QgsRelation::RelationStrength relationStrength ) 372 : : { 373 : 0 : d.detach(); 374 : 0 : d->mRelationStrength = relationStrength; 375 : 0 : updateRelationStatus(); 376 : 0 : } 377 : : 378 : 0 : QList<QgsRelation> QgsPolymorphicRelation::generateRelations() const 379 : : { 380 : 0 : QList<QgsRelation> relations; 381 : : 382 : 0 : if ( !isValid() ) 383 : 0 : return relations; 384 : : 385 : 0 : const QStringList referencedLayerIds = d->mReferencedLayerIds; 386 : : 387 : 0 : for ( const QString &referencedLayerId : referencedLayerIds ) 388 : : { 389 : 0 : QgsRelation relation; 390 : 0 : QString referencedLayerName = d->mReferencedLayersMap[referencedLayerId]->name(); 391 : : 392 : 0 : relation.setId( QStringLiteral( "%1_%2" ).arg( d->mRelationId, referencedLayerName ) ); 393 : 0 : relation.setReferencedLayer( referencedLayerId ); 394 : 0 : relation.setReferencingLayer( d->mReferencingLayerId ); 395 : 0 : relation.setName( QStringLiteral( "Generated for \"%1\"" ).arg( referencedLayerName ) ); 396 : 0 : relation.setPolymorphicRelationId( d->mRelationId ); 397 : 0 : relation.setStrength( d->mRelationStrength ); 398 : : 399 : 0 : const QList<QgsRelation::FieldPair> constFieldPairs = fieldPairs(); 400 : 0 : for ( const QgsRelation::FieldPair &fieldPair : constFieldPairs ) 401 : 0 : relation.addFieldPair( fieldPair ); 402 : : 403 : 0 : if ( !relation.isValid() ) 404 : 0 : continue; 405 : : 406 : 0 : relations << relation; 407 : 0 : } 408 : : 409 : 0 : return relations; 410 : 0 : } 411 : : 412 : 0 : QString QgsPolymorphicRelation::layerRepresentation( const QgsVectorLayer *layer ) const 413 : : { 414 : 0 : if ( !layer || !layer->isValid() ) 415 : 0 : return QString(); 416 : : 417 : 0 : QgsExpressionContext context = layer->createExpressionContext(); 418 : 0 : QgsExpression expr( d->mReferencedLayerExpression ); 419 : : 420 : 0 : return expr.evaluate( &context ).toString(); 421 : 0 : }