Quantum GIS API Documentation
1.7.4
|
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 }