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