Quantum GIS API Documentation  1.8
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       //skip the join field to avoid double field names (fields often have the same name)
00100       if ( fieldIt.key() != joinIt->joinField )
00101       {
00102         fields.insert( maxIndex + 1 + fieldIt.key(), fieldIt.value() );
00103       }
00104     }
00105 
00106     if ( maximumIndex( joinFields, currentMaxIndex ) )
00107     {
00108       maxIndex += ( currentMaxIndex + 1 ); //+1 because there are fields with index 0
00109     }
00110   }
00111 }
00112 
00113 void QgsVectorLayerJoinBuffer::createJoinCaches()
00114 {
00115   QList< QgsVectorJoinInfo >::iterator joinIt = mVectorJoins.begin();
00116   for ( ; joinIt != mVectorJoins.end(); ++joinIt )
00117   {
00118     cacheJoinLayer( *joinIt );
00119   }
00120 }
00121 
00122 void QgsVectorLayerJoinBuffer::select( const QgsAttributeList& fetchAttributes,
00123                                        QgsAttributeList& sourceJoinFields, int maxProviderIndex )
00124 {
00125   mFetchJoinInfos.clear();
00126   sourceJoinFields.clear();
00127 
00128   QgsAttributeList::const_iterator attIt = fetchAttributes.constBegin();
00129   for ( ; attIt != fetchAttributes.constEnd(); ++attIt )
00130   {
00131     int indexOffset;
00132     const QgsVectorJoinInfo* joinInfo = joinForFieldIndex( *attIt, maxProviderIndex, indexOffset );
00133     if ( joinInfo )
00134     {
00135       QgsVectorLayer* joinLayer = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( joinInfo->joinLayerId ) );
00136       if ( joinLayer )
00137       {
00138         mFetchJoinInfos[ joinLayer ].joinInfo = joinInfo;
00139         mFetchJoinInfos[ joinLayer].attributes.push_back( *attIt - indexOffset ); //store provider index
00140         mFetchJoinInfos[ joinLayer ].indexOffset = indexOffset;
00141         //for joined fields, we always need to request the targetField from the provider too
00142         if ( !fetchAttributes.contains( joinInfo->targetField ) )
00143         {
00144           sourceJoinFields << joinInfo->targetField;
00145         }
00146       }
00147     }
00148   }
00149 }
00150 
00151 void QgsVectorLayerJoinBuffer::updateFeatureAttributes( QgsFeature &f, int maxProviderIndex, bool all )
00152 {
00153   if ( all )
00154   {
00155     int index = maxProviderIndex + 1;
00156     int currentMaxIndex;
00157 
00158     QList< QgsVectorJoinInfo >::const_iterator joinIt = mVectorJoins.constBegin();
00159     for ( ; joinIt != mVectorJoins.constEnd(); ++joinIt )
00160     {
00161       QgsVectorLayer* joinLayer = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( joinIt->joinLayerId ) );
00162       if ( !joinLayer )
00163       {
00164         continue;
00165       }
00166 
00167       QString joinFieldName = joinLayer->pendingFields().value( joinIt->joinField ).name();
00168       if ( joinFieldName.isEmpty() )
00169       {
00170         continue;
00171       }
00172 
00173       QVariant targetFieldValue = f.attributeMap().value( joinIt->targetField );
00174       if ( !targetFieldValue.isValid() )
00175       {
00176         continue;
00177       }
00178 
00179       addJoinedFeatureAttributes( f, *joinIt, joinFieldName, targetFieldValue, joinLayer->pendingAllAttributesList(), index );
00180 
00181       maximumIndex( joinLayer->pendingFields(), currentMaxIndex );
00182       index += ( currentMaxIndex + 1 );
00183     }
00184   }
00185   else
00186   {
00187     QMap<QgsVectorLayer*, QgsFetchJoinInfo>::const_iterator joinIt = mFetchJoinInfos.constBegin();
00188     for ( ; joinIt != mFetchJoinInfos.constEnd(); ++joinIt )
00189     {
00190       QgsVectorLayer* joinLayer = joinIt.key();
00191       if ( !joinLayer )
00192       {
00193         continue;
00194       }
00195 
00196       QString joinFieldName = joinLayer->pendingFields().value( joinIt.value().joinInfo->joinField ).name();
00197       if ( joinFieldName.isEmpty() )
00198       {
00199         continue;
00200       }
00201 
00202       QVariant targetFieldValue = f.attributeMap().value( joinIt->joinInfo->targetField );
00203       if ( !targetFieldValue.isValid() )
00204       {
00205         continue;
00206       }
00207 
00208       addJoinedFeatureAttributes( f, *( joinIt.value().joinInfo ), joinFieldName, targetFieldValue, joinIt.value().attributes, joinIt.value().indexOffset );
00209     }
00210   }
00211 }
00212 
00213 void QgsVectorLayerJoinBuffer::addJoinedFeatureAttributes( QgsFeature& f, const QgsVectorJoinInfo& joinInfo, const QString& joinFieldName,
00214     const QVariant& joinValue, const QgsAttributeList& attributes, int attributeIndexOffset )
00215 {
00216   const QHash< QString, QgsAttributeMap>& memoryCache = joinInfo.cachedAttributes;
00217   if ( !memoryCache.isEmpty() ) //use join memory cache
00218   {
00219     QgsAttributeMap featureAttributes = memoryCache.value( joinValue.toString() );
00220     bool found = !featureAttributes.isEmpty();
00221     QgsAttributeList::const_iterator attIt = attributes.constBegin();
00222     for ( ; attIt != attributes.constEnd(); ++attIt )
00223     {
00224       //skip the join field to avoid double field names (fields often have the same name)
00225       if ( *attIt == joinInfo.joinField )
00226       {
00227         continue;
00228       }
00229 
00230       if ( found )
00231       {
00232         f.addAttribute( *attIt + attributeIndexOffset, featureAttributes.value( *attIt ) );
00233       }
00234       else
00235       {
00236         f.addAttribute( *attIt + attributeIndexOffset, QVariant() );
00237       }
00238     }
00239   }
00240   else //work with subset string
00241   {
00242     QgsVectorLayer* joinLayer = dynamic_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( joinInfo.joinLayerId ) );
00243     if ( !joinLayer )
00244     {
00245       return;
00246     }
00247 
00248     //no memory cache, query the joined values by setting substring
00249     QString subsetString = joinLayer->dataProvider()->subsetString(); //provider might already have a subset string
00250     QString bkSubsetString = subsetString;
00251     if ( !subsetString.isEmpty() )
00252     {
00253       subsetString.append( " AND " );
00254     }
00255 
00256     subsetString.append( "\"" + joinFieldName + "\"" + " = " + "\"" + joinValue.toString() + "\"" );
00257     joinLayer->dataProvider()->setSubsetString( subsetString, false );
00258 
00259     //select (no geometry)
00260     joinLayer->select( attributes, QgsRectangle(), false, false );
00261 
00262     //get first feature
00263     QgsFeature fet;
00264     if ( joinLayer->nextFeature( fet ) )
00265     {
00266       QgsAttributeMap attMap = fet.attributeMap();
00267       QgsAttributeMap::const_iterator attIt = attMap.constBegin();
00268       for ( ; attIt != attMap.constEnd(); ++attIt )
00269       {
00270         f.addAttribute( attIt.key() + attributeIndexOffset, attIt.value() );
00271       }
00272     }
00273     else //no suitable join feature found, insert invalid variants
00274     {
00275       QgsAttributeList::const_iterator attIt = attributes.constBegin();
00276       for ( ; attIt != attributes.constEnd(); ++attIt )
00277       {
00278         f.addAttribute( *attIt + attributeIndexOffset, QVariant() );
00279       }
00280     }
00281 
00282     joinLayer->dataProvider()->setSubsetString( bkSubsetString, false );
00283   }
00284 }
00285 
00286 void QgsVectorLayerJoinBuffer::writeXml( QDomNode& layer_node, QDomDocument& document ) const
00287 {
00288   QDomElement vectorJoinsElem = document.createElement( "vectorjoins" );
00289   layer_node.appendChild( vectorJoinsElem );
00290   QList< QgsVectorJoinInfo >::const_iterator joinIt = mVectorJoins.constBegin();
00291   for ( ; joinIt != mVectorJoins.constEnd(); ++joinIt )
00292   {
00293     QDomElement joinElem = document.createElement( "join" );
00294     joinElem.setAttribute( "targetField", joinIt->targetField );
00295     joinElem.setAttribute( "joinLayerId", joinIt->joinLayerId );
00296     joinElem.setAttribute( "joinField", joinIt->joinField );
00297     joinElem.setAttribute( "memoryCache", !joinIt->cachedAttributes.isEmpty() );
00298     vectorJoinsElem.appendChild( joinElem );
00299   }
00300 }
00301 
00302 void QgsVectorLayerJoinBuffer::readXml( const QDomNode& layer_node )
00303 {
00304   mVectorJoins.clear();
00305   QDomElement vectorJoinsElem = layer_node.firstChildElement( "vectorjoins" );
00306   if ( !vectorJoinsElem.isNull() )
00307   {
00308     QDomNodeList joinList = vectorJoinsElem.elementsByTagName( "join" );
00309     for ( int i = 0; i < joinList.size(); ++i )
00310     {
00311       QDomElement infoElem = joinList.at( i ).toElement();
00312       QgsVectorJoinInfo info;
00313       info.joinField = infoElem.attribute( "joinField" ).toInt();
00314       info.joinLayerId = infoElem.attribute( "joinLayerId" );
00315       info.targetField = infoElem.attribute( "targetField" ).toInt();
00316       info.memoryCache = infoElem.attribute( "memoryCache" ).toInt();
00317       addJoin( info );
00318     }
00319   }
00320 }
00321 
00322 const QgsVectorJoinInfo* QgsVectorLayerJoinBuffer::joinForFieldIndex( int index, int maxProviderIndex, int& indexOffset ) const
00323 {
00324   int currentMaxIndex = 0;
00325   int totIndex = maxProviderIndex + 1;
00326 
00327   //go through all the joins to search the index
00328   QList< QgsVectorJoinInfo>::const_iterator joinIt = mVectorJoins.constBegin();
00329   for ( ; joinIt != mVectorJoins.constEnd(); ++joinIt )
00330   {
00331     QgsVectorLayer* joinLayer = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( joinIt->joinLayerId ) );
00332     if ( !joinLayer )
00333     {
00334       continue;
00335     }
00336 
00337     if ( joinLayer->pendingFields().contains( index - totIndex ) )
00338     {
00339       indexOffset = totIndex;
00340       return &( *joinIt );
00341     }
00342 
00343     maximumIndex( joinLayer->pendingFields(), currentMaxIndex );
00344     totIndex += ( currentMaxIndex + 1 );
00345   }
00346 
00347   //an added field or a provider field
00348   return 0;
00349 }
00350 
00351 bool QgsVectorLayerJoinBuffer::maximumIndex( const QgsFieldMap& fMap, int& index )
00352 {
00353   if ( fMap.size() < 1 )
00354   {
00355     return false;
00356   }
00357   QgsFieldMap::const_iterator endIt = fMap.constEnd();
00358   --endIt;
00359   index = endIt.key();
00360   return true;
00361 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines