Quantum GIS API Documentation  1.7.5-Wroclaw
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
qgsvectorlayerjoinbuffer.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsvectorlayerjoinbuffer.cpp
3  ----------------------------
4  begin : Feb 09, 2011
5  copyright : (C) 2011 by Marco Hugentobler
6  email : marco dot hugentobler at sourcepole dot ch
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
19 #include "qgsmaplayerregistry.h"
20 #include "qgsvectordataprovider.h"
21 
22 #include <QDomElement>
23 
25 {
26 }
27 
29 {
30 }
31 
33 {
34  mVectorJoins.push_back( joinInfo );
35 
36  //cache joined layer to virtual memory if specified by user
37  if ( joinInfo.memoryCache )
38  {
39  cacheJoinLayer( mVectorJoins.last() );
40  }
41 }
42 
43 void QgsVectorLayerJoinBuffer::removeJoin( const QString& joinLayerId )
44 {
45  for ( int i = 0; i < mVectorJoins.size(); ++i )
46  {
47  if ( mVectorJoins.at( i ).joinLayerId == joinLayerId )
48  {
49  mVectorJoins.removeAt( i );
50  //remove corresponding fetch join info
51  QgsVectorLayer* joinLayer = dynamic_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( joinLayerId ) );
52  if ( joinLayer )
53  {
54  mFetchJoinInfos.remove( joinLayer );
55  }
56  }
57  }
58 }
59 
61 {
62  //memory cache not required or already done
63  if ( !joinInfo.memoryCache || joinInfo.cachedAttributes.size() > 0 )
64  {
65  return;
66  }
67 
68  QgsVectorLayer* cacheLayer = dynamic_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( joinInfo.joinLayerId ) );
69  if ( cacheLayer )
70  {
71  joinInfo.cachedAttributes.clear();
72  cacheLayer->select( cacheLayer->pendingAllAttributesList(), QgsRectangle(), false, false );
73  QgsFeature f;
74  while ( cacheLayer->nextFeature( f ) )
75  {
76  const QgsAttributeMap& map = f.attributeMap();
77  joinInfo.cachedAttributes.insert( map.value( joinInfo.joinField ).toString(), map );
78  }
79  }
80 }
81 
83 {
84  int currentMaxIndex = 0; //maximum index of the current join layer
85 
86  QList< QgsVectorJoinInfo>::const_iterator joinIt = mVectorJoins.constBegin();
87  for ( ; joinIt != mVectorJoins.constEnd(); ++joinIt )
88  {
89  QgsVectorLayer* joinLayer = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( joinIt->joinLayerId ) );
90  if ( !joinLayer )
91  {
92  continue;
93  }
94 
95  const QgsFieldMap& joinFields = joinLayer->pendingFields();
96  QgsFieldMap::const_iterator fieldIt = joinFields.constBegin();
97  for ( ; fieldIt != joinFields.constEnd(); ++fieldIt )
98  {
99  fields.insert( maxIndex + 1 + fieldIt.key(), fieldIt.value() );
100  }
101 
102  if ( maximumIndex( joinFields, currentMaxIndex ) )
103  {
104  maxIndex += ( currentMaxIndex + 1 ); //+1 because there are fields with index 0
105  }
106  }
107 }
108 
110 {
111  QList< QgsVectorJoinInfo >::iterator joinIt = mVectorJoins.begin();
112  for ( ; joinIt != mVectorJoins.end(); ++joinIt )
113  {
114  cacheJoinLayer( *joinIt );
115  }
116 }
117 
119  QgsAttributeList& sourceJoinFields, int maxProviderIndex )
120 {
121  mFetchJoinInfos.clear();
122  sourceJoinFields.clear();
123 
124  QgsAttributeList::const_iterator attIt = fetchAttributes.constBegin();
125  for ( ; attIt != fetchAttributes.constEnd(); ++attIt )
126  {
127  int indexOffset;
128  const QgsVectorJoinInfo* joinInfo = joinForFieldIndex( *attIt, maxProviderIndex, indexOffset );
129  if ( joinInfo )
130  {
131  QgsVectorLayer* joinLayer = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( joinInfo->joinLayerId ) );
132  if ( joinLayer )
133  {
134  mFetchJoinInfos[ joinLayer ].joinInfo = joinInfo;
135  mFetchJoinInfos[ joinLayer].attributes.push_back( *attIt - indexOffset ); //store provider index
136  mFetchJoinInfos[ joinLayer ].indexOffset = indexOffset;
137  //for joined fields, we always need to request the targetField from the provider too
138  if ( !fetchAttributes.contains( joinInfo->targetField ) )
139  {
140  sourceJoinFields << joinInfo->targetField;
141  }
142  }
143  }
144  }
145 }
146 
147 void QgsVectorLayerJoinBuffer::updateFeatureAttributes( QgsFeature &f, int maxProviderIndex, bool all )
148 {
149  if ( all )
150  {
151  int index = maxProviderIndex + 1;
152  int currentMaxIndex;
153 
154  QList< QgsVectorJoinInfo >::const_iterator joinIt = mVectorJoins.constBegin();
155  for ( ; joinIt != mVectorJoins.constEnd(); ++joinIt )
156  {
157  QgsVectorLayer* joinLayer = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( joinIt->joinLayerId ) );
158  if ( !joinLayer )
159  {
160  continue;
161  }
162 
163  QString joinFieldName = joinLayer->pendingFields().value( joinIt->joinField ).name();
164  if ( joinFieldName.isEmpty() )
165  {
166  continue;
167  }
168 
169  QVariant targetFieldValue = f.attributeMap().value( joinIt->targetField );
170  if ( !targetFieldValue.isValid() )
171  {
172  continue;
173  }
174 
175  addJoinedFeatureAttributes( f, *joinIt, joinFieldName, targetFieldValue, joinLayer->pendingAllAttributesList(), index );
176 
177  maximumIndex( joinLayer->pendingFields(), currentMaxIndex );
178  index += ( currentMaxIndex + 1 );
179  }
180  }
181  else
182  {
183  QMap<QgsVectorLayer*, QgsFetchJoinInfo>::const_iterator joinIt = mFetchJoinInfos.constBegin();
184  for ( ; joinIt != mFetchJoinInfos.constEnd(); ++joinIt )
185  {
186  QgsVectorLayer* joinLayer = joinIt.key();
187  if ( !joinLayer )
188  {
189  continue;
190  }
191 
192  QString joinFieldName = joinLayer->pendingFields().value( joinIt.value().joinInfo->joinField ).name();
193  if ( joinFieldName.isEmpty() )
194  {
195  continue;
196  }
197 
198  QVariant targetFieldValue = f.attributeMap().value( joinIt->joinInfo->targetField );
199  if ( !targetFieldValue.isValid() )
200  {
201  continue;
202  }
203 
204  addJoinedFeatureAttributes( f, *( joinIt.value().joinInfo ), joinFieldName, targetFieldValue, joinIt.value().attributes, joinIt.value().indexOffset );
205  }
206  }
207 }
208 
209 void QgsVectorLayerJoinBuffer::addJoinedFeatureAttributes( QgsFeature& f, const QgsVectorJoinInfo& joinInfo, const QString& joinFieldName,
210  const QVariant& joinValue, const QgsAttributeList& attributes, int attributeIndexOffset )
211 {
212  const QHash< QString, QgsAttributeMap>& memoryCache = joinInfo.cachedAttributes;
213  if ( !memoryCache.isEmpty() ) //use join memory cache
214  {
215  QgsAttributeMap featureAttributes = memoryCache.value( joinValue.toString() );
216  bool found = !featureAttributes.isEmpty();
217  QgsAttributeList::const_iterator attIt = attributes.constBegin();
218  for ( ; attIt != attributes.constEnd(); ++attIt )
219  {
220  if ( found )
221  {
222  f.addAttribute( *attIt + attributeIndexOffset, featureAttributes.value( *attIt ) );
223  }
224  else
225  {
226  f.addAttribute( *attIt + attributeIndexOffset, QVariant() );
227  }
228  }
229  }
230  else //work with subset string
231  {
232  QgsVectorLayer* joinLayer = dynamic_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( joinInfo.joinLayerId ) );
233  if ( !joinLayer )
234  {
235  return;
236  }
237 
238  //no memory cache, query the joined values by setting substring
239  QString subsetString = joinLayer->dataProvider()->subsetString(); //provider might already have a subset string
240  QString bkSubsetString = subsetString;
241  if ( !subsetString.isEmpty() )
242  {
243  subsetString.append( " AND " );
244  }
245 
246  subsetString.append( "\"" + joinFieldName + "\"" + " = " + "\"" + joinValue.toString() + "\"" );
247  joinLayer->dataProvider()->setSubsetString( subsetString, false );
248 
249  //select (no geometry)
250  joinLayer->select( attributes, QgsRectangle(), false, false );
251 
252  //get first feature
253  QgsFeature fet;
254  if ( joinLayer->nextFeature( fet ) )
255  {
256  QgsAttributeMap attMap = fet.attributeMap();
257  QgsAttributeMap::const_iterator attIt = attMap.constBegin();
258  for ( ; attIt != attMap.constEnd(); ++attIt )
259  {
260  f.addAttribute( attIt.key() + attributeIndexOffset, attIt.value() );
261  }
262  }
263  else //no suitable join feature found, insert invalid variants
264  {
265  QgsAttributeList::const_iterator attIt = attributes.constBegin();
266  for ( ; attIt != attributes.constEnd(); ++attIt )
267  {
268  f.addAttribute( *attIt + attributeIndexOffset, QVariant() );
269  }
270  }
271 
272  joinLayer->dataProvider()->setSubsetString( bkSubsetString, false );
273  }
274 }
275 
276 void QgsVectorLayerJoinBuffer::writeXml( QDomNode& layer_node, QDomDocument& document ) const
277 {
278  QDomElement vectorJoinsElem = document.createElement( "vectorjoins" );
279  layer_node.appendChild( vectorJoinsElem );
280  QList< QgsVectorJoinInfo >::const_iterator joinIt = mVectorJoins.constBegin();
281  for ( ; joinIt != mVectorJoins.constEnd(); ++joinIt )
282  {
283  QDomElement joinElem = document.createElement( "join" );
284  joinElem.setAttribute( "targetField", joinIt->targetField );
285  joinElem.setAttribute( "joinLayerId", joinIt->joinLayerId );
286  joinElem.setAttribute( "joinField", joinIt->joinField );
287  joinElem.setAttribute( "memoryCache", !joinIt->cachedAttributes.isEmpty() );
288  vectorJoinsElem.appendChild( joinElem );
289  }
290 }
291 
292 void QgsVectorLayerJoinBuffer::readXml( QDomNode& layer_node )
293 {
294  mVectorJoins.clear();
295  QDomElement vectorJoinsElem = layer_node.firstChildElement( "vectorjoins" );
296  if ( !vectorJoinsElem.isNull() )
297  {
298  QDomNodeList joinList = vectorJoinsElem.elementsByTagName( "join" );
299  for ( int i = 0; i < joinList.size(); ++i )
300  {
301  QDomElement infoElem = joinList.at( i ).toElement();
302  QgsVectorJoinInfo info;
303  info.joinField = infoElem.attribute( "joinField" ).toInt();
304  info.joinLayerId = infoElem.attribute( "joinLayerId" );
305  info.targetField = infoElem.attribute( "targetField" ).toInt();
306  info.memoryCache = infoElem.attribute( "memoryCache" ).toInt();
307  addJoin( info );
308  }
309  }
310 }
311 
312 const QgsVectorJoinInfo* QgsVectorLayerJoinBuffer::joinForFieldIndex( int index, int maxProviderIndex, int& indexOffset ) const
313 {
314  int currentMaxIndex = 0;
315  int totIndex = maxProviderIndex + 1;
316 
317  //go through all the joins to search the index
318  QList< QgsVectorJoinInfo>::const_iterator joinIt = mVectorJoins.constBegin();
319  for ( ; joinIt != mVectorJoins.constEnd(); ++joinIt )
320  {
321  QgsVectorLayer* joinLayer = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( joinIt->joinLayerId ) );
322  if ( !joinLayer )
323  {
324  continue;
325  }
326 
327  if ( joinLayer->pendingFields().contains( index - totIndex ) )
328  {
329  indexOffset = totIndex;
330  return &( *joinIt );
331  }
332 
333  maximumIndex( joinLayer->pendingFields(), currentMaxIndex );
334  totIndex += ( currentMaxIndex + 1 );
335  }
336 
337  //an added field or a provider field
338  return 0;
339 }
340 
342 {
343  if ( fMap.size() < 1 )
344  {
345  return false;
346  }
347  QgsFieldMap::const_iterator endIt = fMap.constEnd();
348  --endIt;
349  index = endIt.key();
350  return true;
351 }