Quantum GIS API Documentation  1.7.4
src/core/qgsvectorlayerjoinbuffer.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002                           qgsvectorlayerjoinbuffer.cpp
00003                           ----------------------------
00004     begin                : Feb 09, 2011
00005     copyright            : (C) 2011 by Marco Hugentobler
00006     email                : marco dot hugentobler at sourcepole dot ch
00007  ***************************************************************************/
00008 
00009 /***************************************************************************
00010  *                                                                         *
00011  *   This program is free software; you can redistribute it and/or modify  *
00012  *   it under the terms of the GNU General Public License as published by  *
00013  *   the Free Software Foundation; either version 2 of the License, or     *
00014  *   (at your option) any later version.                                   *
00015  *                                                                         *
00016  ***************************************************************************/
00017 
00018 #include "qgsvectorlayerjoinbuffer.h"
00019 #include "qgsmaplayerregistry.h"
00020 #include "qgsvectordataprovider.h"
00021 
00022 #include <QDomElement>
00023 
00024 QgsVectorLayerJoinBuffer::QgsVectorLayerJoinBuffer()
00025 {
00026 }
00027 
00028 QgsVectorLayerJoinBuffer::~QgsVectorLayerJoinBuffer()
00029 {
00030 }
00031 
00032 void QgsVectorLayerJoinBuffer::addJoin( QgsVectorJoinInfo joinInfo )
00033 {
00034   mVectorJoins.push_back( joinInfo );
00035 
00036   //cache joined layer to virtual memory if specified by user
00037   if ( joinInfo.memoryCache )
00038   {
00039     cacheJoinLayer( mVectorJoins.last() );
00040   }
00041 }
00042 
00043 void QgsVectorLayerJoinBuffer::removeJoin( const QString& joinLayerId )
00044 {
00045   for ( int i = 0; i < mVectorJoins.size(); ++i )
00046   {
00047     if ( mVectorJoins.at( i ).joinLayerId == joinLayerId )
00048     {
00049       mVectorJoins.removeAt( i );
00050       //remove corresponding fetch join info
00051       QgsVectorLayer* joinLayer = dynamic_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( joinLayerId ) );
00052       if ( joinLayer )
00053       {
00054         mFetchJoinInfos.remove( joinLayer );
00055       }
00056     }
00057   }
00058 }
00059 
00060 void QgsVectorLayerJoinBuffer::cacheJoinLayer( QgsVectorJoinInfo& joinInfo )
00061 {
00062   //memory cache not required or already done
00063   if ( !joinInfo.memoryCache || joinInfo.cachedAttributes.size() > 0 )
00064   {
00065     return;
00066   }
00067 
00068   QgsVectorLayer* cacheLayer = dynamic_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( joinInfo.joinLayerId ) );
00069   if ( cacheLayer )
00070   {
00071     joinInfo.cachedAttributes.clear();
00072     cacheLayer->select( cacheLayer->pendingAllAttributesList(), QgsRectangle(), false, false );
00073     QgsFeature f;
00074     while ( cacheLayer->nextFeature( f ) )
00075     {
00076       const QgsAttributeMap& map = f.attributeMap();
00077       joinInfo.cachedAttributes.insert( map.value( joinInfo.joinField ).toString(), map );
00078     }
00079   }
00080 }
00081 
00082 void QgsVectorLayerJoinBuffer::updateFieldMap( QgsFieldMap& fields, int& maxIndex )
00083 {
00084   int currentMaxIndex = 0; //maximum index of the current join layer
00085 
00086   QList< QgsVectorJoinInfo>::const_iterator joinIt = mVectorJoins.constBegin();
00087   for ( ; joinIt != mVectorJoins.constEnd(); ++joinIt )
00088   {
00089     QgsVectorLayer* joinLayer = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( joinIt->joinLayerId ) );
00090     if ( !joinLayer )
00091     {
00092       continue;
00093     }
00094 
00095     const QgsFieldMap& joinFields = joinLayer->pendingFields();
00096     QgsFieldMap::const_iterator fieldIt = joinFields.constBegin();
00097     for ( ; fieldIt != joinFields.constEnd(); ++fieldIt )
00098     {
00099       fields.insert( maxIndex + 1 + fieldIt.key(), fieldIt.value() );
00100     }
00101 
00102     if ( maximumIndex( joinFields, currentMaxIndex ) )
00103     {
00104       maxIndex += ( currentMaxIndex + 1 ); //+1 because there are fields with index 0
00105     }
00106   }
00107 }
00108 
00109 void QgsVectorLayerJoinBuffer::createJoinCaches()
00110 {
00111   QList< QgsVectorJoinInfo >::iterator joinIt = mVectorJoins.begin();
00112   for ( ; joinIt != mVectorJoins.end(); ++joinIt )
00113   {
00114     cacheJoinLayer( *joinIt );
00115   }
00116 }
00117 
00118 void QgsVectorLayerJoinBuffer::select( const QgsAttributeList& fetchAttributes,
00119                                        QgsAttributeList& sourceJoinFields, int maxProviderIndex )
00120 {
00121   mFetchJoinInfos.clear();
00122   sourceJoinFields.clear();
00123 
00124   QgsAttributeList::const_iterator attIt = fetchAttributes.constBegin();
00125   for ( ; attIt != fetchAttributes.constEnd(); ++attIt )
00126   {
00127     int indexOffset;
00128     const QgsVectorJoinInfo* joinInfo = joinForFieldIndex( *attIt, maxProviderIndex, indexOffset );
00129     if ( joinInfo )
00130     {
00131       QgsVectorLayer* joinLayer = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( joinInfo->joinLayerId ) );
00132       if ( joinLayer )
00133       {
00134         mFetchJoinInfos[ joinLayer ].joinInfo = joinInfo;
00135         mFetchJoinInfos[ joinLayer].attributes.push_back( *attIt - indexOffset ); //store provider index
00136         mFetchJoinInfos[ joinLayer ].indexOffset = indexOffset;
00137         //for joined fields, we always need to request the targetField from the provider too
00138         if ( !fetchAttributes.contains( joinInfo->targetField ) )
00139         {
00140           sourceJoinFields << joinInfo->targetField;
00141         }
00142       }
00143     }
00144   }
00145 }
00146 
00147 void QgsVectorLayerJoinBuffer::updateFeatureAttributes( QgsFeature &f, int maxProviderIndex, bool all )
00148 {
00149   if ( all )
00150   {
00151     int index = maxProviderIndex + 1;
00152     int currentMaxIndex;
00153 
00154     QList< QgsVectorJoinInfo >::const_iterator joinIt = mVectorJoins.constBegin();
00155     for ( ; joinIt != mVectorJoins.constEnd(); ++joinIt )
00156     {
00157       QgsVectorLayer* joinLayer = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( joinIt->joinLayerId ) );
00158       if ( !joinLayer )
00159       {
00160         continue;
00161       }
00162 
00163       QString joinFieldName = joinLayer->pendingFields().value( joinIt->joinField ).name();
00164       if ( joinFieldName.isEmpty() )
00165       {
00166         continue;
00167       }
00168 
00169       QVariant targetFieldValue = f.attributeMap().value( joinIt->targetField );
00170       if ( !targetFieldValue.isValid() )
00171       {
00172         continue;
00173       }
00174 
00175       addJoinedFeatureAttributes( f, *joinIt, joinFieldName, targetFieldValue, joinLayer->pendingAllAttributesList(), index );
00176 
00177       maximumIndex( joinLayer->pendingFields(), currentMaxIndex );
00178       index += ( currentMaxIndex + 1 );
00179     }
00180   }
00181   else
00182   {
00183     QMap<QgsVectorLayer*, QgsFetchJoinInfo>::const_iterator joinIt = mFetchJoinInfos.constBegin();
00184     for ( ; joinIt != mFetchJoinInfos.constEnd(); ++joinIt )
00185     {
00186       QgsVectorLayer* joinLayer = joinIt.key();
00187       if ( !joinLayer )
00188       {
00189         continue;
00190       }
00191 
00192       QString joinFieldName = joinLayer->pendingFields().value( joinIt.value().joinInfo->joinField ).name();
00193       if ( joinFieldName.isEmpty() )
00194       {
00195         continue;
00196       }
00197 
00198       QVariant targetFieldValue = f.attributeMap().value( joinIt->joinInfo->targetField );
00199       if ( !targetFieldValue.isValid() )
00200       {
00201         continue;
00202       }
00203 
00204       addJoinedFeatureAttributes( f, *( joinIt.value().joinInfo ), joinFieldName, targetFieldValue, joinIt.value().attributes, joinIt.value().indexOffset );
00205     }
00206   }
00207 }
00208 
00209 void QgsVectorLayerJoinBuffer::addJoinedFeatureAttributes( QgsFeature& f, const QgsVectorJoinInfo& joinInfo, const QString& joinFieldName,
00210     const QVariant& joinValue, const QgsAttributeList& attributes, int attributeIndexOffset )
00211 {
00212   const QHash< QString, QgsAttributeMap>& memoryCache = joinInfo.cachedAttributes;
00213   if ( !memoryCache.isEmpty() ) //use join memory cache
00214   {
00215     QgsAttributeMap featureAttributes = memoryCache.value( joinValue.toString() );
00216     bool found = !featureAttributes.isEmpty();
00217     QgsAttributeList::const_iterator attIt = attributes.constBegin();
00218     for ( ; attIt != attributes.constEnd(); ++attIt )
00219     {
00220       if ( found )
00221       {
00222         f.addAttribute( *attIt + attributeIndexOffset, featureAttributes.value( *attIt ) );
00223       }
00224       else
00225       {
00226         f.addAttribute( *attIt + attributeIndexOffset, QVariant() );
00227       }
00228     }
00229   }
00230   else //work with subset string
00231   {
00232     QgsVectorLayer* joinLayer = dynamic_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( joinInfo.joinLayerId ) );
00233     if ( !joinLayer )
00234     {
00235       return;
00236     }
00237 
00238     //no memory cache, query the joined values by setting substring
00239     QString subsetString = joinLayer->dataProvider()->subsetString(); //provider might already have a subset string
00240     QString bkSubsetString = subsetString;
00241     if ( !subsetString.isEmpty() )
00242     {
00243       subsetString.append( " AND " );
00244     }
00245 
00246     subsetString.append( "\"" + joinFieldName + "\"" + " = " + "\"" + joinValue.toString() + "\"" );
00247     joinLayer->dataProvider()->setSubsetString( subsetString, false );
00248 
00249     //select (no geometry)
00250     joinLayer->select( attributes, QgsRectangle(), false, false );
00251 
00252     //get first feature
00253     QgsFeature fet;
00254     if ( joinLayer->nextFeature( fet ) )
00255     {
00256       QgsAttributeMap attMap = fet.attributeMap();
00257       QgsAttributeMap::const_iterator attIt = attMap.constBegin();
00258       for ( ; attIt != attMap.constEnd(); ++attIt )
00259       {
00260         f.addAttribute( attIt.key() + attributeIndexOffset, attIt.value() );
00261       }
00262     }
00263     else //no suitable join feature found, insert invalid variants
00264     {
00265       QgsAttributeList::const_iterator attIt = attributes.constBegin();
00266       for ( ; attIt != attributes.constEnd(); ++attIt )
00267       {
00268         f.addAttribute( *attIt + attributeIndexOffset, QVariant() );
00269       }
00270     }
00271 
00272     joinLayer->dataProvider()->setSubsetString( bkSubsetString, false );
00273   }
00274 }
00275 
00276 void QgsVectorLayerJoinBuffer::writeXml( QDomNode& layer_node, QDomDocument& document ) const
00277 {
00278   QDomElement vectorJoinsElem = document.createElement( "vectorjoins" );
00279   layer_node.appendChild( vectorJoinsElem );
00280   QList< QgsVectorJoinInfo >::const_iterator joinIt = mVectorJoins.constBegin();
00281   for ( ; joinIt != mVectorJoins.constEnd(); ++joinIt )
00282   {
00283     QDomElement joinElem = document.createElement( "join" );
00284     joinElem.setAttribute( "targetField", joinIt->targetField );
00285     joinElem.setAttribute( "joinLayerId", joinIt->joinLayerId );
00286     joinElem.setAttribute( "joinField", joinIt->joinField );
00287     joinElem.setAttribute( "memoryCache", !joinIt->cachedAttributes.isEmpty() );
00288     vectorJoinsElem.appendChild( joinElem );
00289   }
00290 }
00291 
00292 void QgsVectorLayerJoinBuffer::readXml( QDomNode& layer_node )
00293 {
00294   mVectorJoins.clear();
00295   QDomElement vectorJoinsElem = layer_node.firstChildElement( "vectorjoins" );
00296   if ( !vectorJoinsElem.isNull() )
00297   {
00298     QDomNodeList joinList = vectorJoinsElem.elementsByTagName( "join" );
00299     for ( int i = 0; i < joinList.size(); ++i )
00300     {
00301       QDomElement infoElem = joinList.at( i ).toElement();
00302       QgsVectorJoinInfo info;
00303       info.joinField = infoElem.attribute( "joinField" ).toInt();
00304       info.joinLayerId = infoElem.attribute( "joinLayerId" );
00305       info.targetField = infoElem.attribute( "targetField" ).toInt();
00306       info.memoryCache = infoElem.attribute( "memoryCache" ).toInt();
00307       addJoin( info );
00308     }
00309   }
00310 }
00311 
00312 const QgsVectorJoinInfo* QgsVectorLayerJoinBuffer::joinForFieldIndex( int index, int maxProviderIndex, int& indexOffset ) const
00313 {
00314   int currentMaxIndex = 0;
00315   int totIndex = maxProviderIndex + 1;
00316 
00317   //go through all the joins to search the index
00318   QList< QgsVectorJoinInfo>::const_iterator joinIt = mVectorJoins.constBegin();
00319   for ( ; joinIt != mVectorJoins.constEnd(); ++joinIt )
00320   {
00321     QgsVectorLayer* joinLayer = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( joinIt->joinLayerId ) );
00322     if ( !joinLayer )
00323     {
00324       continue;
00325     }
00326 
00327     if ( joinLayer->pendingFields().contains( index - totIndex ) )
00328     {
00329       indexOffset = totIndex;
00330       return &( *joinIt );
00331     }
00332 
00333     maximumIndex( joinLayer->pendingFields(), currentMaxIndex );
00334     totIndex += ( currentMaxIndex + 1 );
00335   }
00336 
00337   //an added field or a provider field
00338   return 0;
00339 }
00340 
00341 bool QgsVectorLayerJoinBuffer::maximumIndex( const QgsFieldMap& fMap, int& index )
00342 {
00343   if ( fMap.size() < 1 )
00344   {
00345     return false;
00346   }
00347   QgsFieldMap::const_iterator endIt = fMap.constEnd();
00348   --endIt;
00349   index = endIt.key();
00350   return true;
00351 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines