Skip to content

Commit f1a655f

Browse files
author
mhugent
committedFeb 11, 2011
[FEATURE]: experimental table join support refactored and ported from branch to trunk
git-svn-id: http://svn.osgeo.org/qgis/trunk@15155 c8812cc2-4d05-0410-92ff-de0c093fc19c
1 parent 9f1a59f commit f1a655f

26 files changed

+1325
-61
lines changed
 

‎images/images.qrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@
269269
<file>themes/default/propertyicons/digitising.png</file>
270270
<file>themes/default/propertyicons/general.png</file>
271271
<file>themes/default/propertyicons/histogram.png</file>
272+
<file>themes/default/propertyicons/join.png</file>
272273
<file>themes/default/propertyicons/labels.png</file>
273274
<file>themes/default/propertyicons/locale.png</file>
274275
<file>themes/default/propertyicons/map_tools.png</file>
3.92 KB
Loading

‎src/app/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ SET(QGIS_APP_SRCS
44
qgisappinterface.cpp
55
qgsabout.cpp
66
qgsaddattrdialog.cpp
7+
qgsaddjoindialog.cpp
78
qgsannotationwidget.cpp
89
qgsattributeactiondialog.cpp
910
qgsattributedialog.cpp
@@ -144,6 +145,7 @@ SET (QGIS_APP_MOC_HDRS
144145
qgsabout.h
145146
qgsaddattrdialog.h
146147
qgsdisplayangle.h
148+
qgsaddjoindialog.h
147149
qgsannotationwidget.h
148150
qgsattributeactiondialog.h
149151
qgsattributedialog.h

‎src/app/attributetable/qgsattributetablemodel.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ void QgsAttributeTableModel::loadLayer()
234234
rect = QgisApp::instance()->mapCanvas()->extent();
235235
}
236236

237-
mLayer->select( mAttributes, rect, false );
237+
mLayer->select( QgsAttributeList(), rect, false );
238238

239239
QgsFeature f;
240240
for ( int i = 0; mLayer->nextFeature( f ); ++i )

‎src/app/qgsaddjoindialog.cpp

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
/***************************************************************************
2+
qgsaddjoindialog.cpp
3+
--------------------
4+
begin : July 10, 2010
5+
copyright : (C) 2010 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+
18+
#include "qgsaddjoindialog.h"
19+
#include "qgsmaplayer.h"
20+
#include "qgsmaplayerregistry.h"
21+
#include "qgsvectordataprovider.h"
22+
#include "qgsvectorlayer.h"
23+
24+
QgsAddJoinDialog::QgsAddJoinDialog( QgsVectorLayer* layer, QWidget * parent, Qt::WindowFlags f ): QDialog( parent, f ), mLayer( layer )
25+
{
26+
setupUi( this );
27+
28+
if ( !mLayer )
29+
{
30+
return;
31+
}
32+
33+
//insert possible vector layers into mJoinLayerComboBox
34+
35+
mJoinLayerComboBox->blockSignals( true );
36+
const QMap<QString, QgsMapLayer*>& layerList = QgsMapLayerRegistry::instance()->mapLayers();
37+
QMap<QString, QgsMapLayer*>::const_iterator layerIt = layerList.constBegin();
38+
for ( ; layerIt != layerList.constEnd(); ++layerIt )
39+
{
40+
QgsMapLayer* currentLayer = layerIt.value();
41+
if ( currentLayer->type() == QgsMapLayer::VectorLayer )
42+
{
43+
QgsVectorLayer* currentVectorLayer = dynamic_cast<QgsVectorLayer*>( currentLayer );
44+
if ( currentVectorLayer && currentVectorLayer != mLayer )
45+
{
46+
if ( currentVectorLayer->dataProvider() && currentVectorLayer->dataProvider()->supportsSubsetString() )
47+
mJoinLayerComboBox->addItem( currentLayer->name(), QVariant( currentLayer->getLayerID() ) );
48+
}
49+
}
50+
}
51+
mJoinLayerComboBox->blockSignals( false );
52+
on_mJoinLayerComboBox_currentIndexChanged( mJoinLayerComboBox->currentIndex() );
53+
54+
//insert possible target fields
55+
const QgsFieldMap& layerFieldMap = mLayer->pendingFields();
56+
QgsFieldMap::const_iterator fieldIt = layerFieldMap.constBegin();
57+
for ( ; fieldIt != layerFieldMap.constEnd(); ++fieldIt )
58+
{
59+
mTargetFieldComboBox->addItem( fieldIt.value().name(), fieldIt.key() );
60+
}
61+
62+
mCacheInMemoryCheckBox->setChecked( true );
63+
}
64+
65+
QgsAddJoinDialog::~QgsAddJoinDialog()
66+
{
67+
}
68+
69+
QString QgsAddJoinDialog::joinedLayerId() const
70+
{
71+
return mJoinLayerComboBox->itemData( mJoinLayerComboBox->currentIndex() ).toString();
72+
}
73+
74+
int QgsAddJoinDialog::joinField() const
75+
{
76+
return mJoinFieldComboBox->itemData( mJoinFieldComboBox->currentIndex() ).toInt();
77+
}
78+
79+
QString QgsAddJoinDialog::joinFieldName() const
80+
{
81+
return mJoinFieldComboBox->itemText( mJoinFieldComboBox->currentIndex() );
82+
}
83+
84+
int QgsAddJoinDialog::targetField() const
85+
{
86+
return mTargetFieldComboBox->itemData( mTargetFieldComboBox->currentIndex() ).toInt();
87+
}
88+
89+
QString QgsAddJoinDialog::targetFieldName() const
90+
{
91+
return mTargetFieldComboBox->itemText( mTargetFieldComboBox->currentIndex() );
92+
}
93+
94+
bool QgsAddJoinDialog::cacheInMemory() const
95+
{
96+
return mCacheInMemoryCheckBox->isChecked();
97+
}
98+
99+
bool QgsAddJoinDialog::createAttributeIndex() const
100+
{
101+
return mCreateIndexCheckBox->isChecked();
102+
}
103+
104+
void QgsAddJoinDialog::on_mJoinLayerComboBox_currentIndexChanged( int index )
105+
{
106+
mJoinFieldComboBox->clear();
107+
QString layerId = mJoinLayerComboBox->itemData( index ).toString();
108+
QgsMapLayer* layer = QgsMapLayerRegistry::instance()->mapLayer( layerId );
109+
if ( !layer )
110+
{
111+
return;
112+
}
113+
QgsVectorLayer* vLayer = dynamic_cast<QgsVectorLayer*>( layer );
114+
if ( !vLayer )
115+
{
116+
return;
117+
}
118+
119+
const QgsFieldMap& layerFieldMap = vLayer->pendingFields();
120+
QgsFieldMap::const_iterator fieldIt = layerFieldMap.constBegin();
121+
for ( ; fieldIt != layerFieldMap.constEnd(); ++fieldIt )
122+
{
123+
mJoinFieldComboBox->addItem( fieldIt.value().name(), fieldIt.key() );
124+
}
125+
126+
//does provider support creation of attribute indices?
127+
QgsVectorDataProvider* dp = vLayer->dataProvider();
128+
if ( dp && ( dp->capabilities() & QgsVectorDataProvider::CreateAttributeIndex ) )
129+
{
130+
mCreateIndexCheckBox->setEnabled( true );
131+
}
132+
else
133+
{
134+
mCreateIndexCheckBox->setEnabled( false );
135+
mCreateIndexCheckBox->setChecked( false );
136+
}
137+
}

‎src/app/qgsaddjoindialog.h

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/***************************************************************************
2+
qgsaddjoindialog.h
3+
------------------
4+
begin : July 10, 2010
5+
copyright : (C) 2010 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+
18+
#ifndef QGSADDJOINDIALOG_H
19+
#define QGSADDJOINDIALOG_H
20+
21+
#include "ui_qgsaddjoindialogbase.h"
22+
class QgsVectorLayer;
23+
24+
class QgsAddJoinDialog: public QDialog, private Ui::QgsAddJoinDialogBase
25+
{
26+
Q_OBJECT
27+
public:
28+
QgsAddJoinDialog( QgsVectorLayer* layer, QWidget * parent = 0, Qt::WindowFlags f = 0 );
29+
~QgsAddJoinDialog();
30+
31+
//retrieve results
32+
33+
/**Get the id of the layer to join*/
34+
QString joinedLayerId() const;
35+
/**Returns the index of the join field*/
36+
int joinField() const;
37+
/**Returns the name of the join field*/
38+
QString joinFieldName() const;
39+
/**Returns the index of the target field (join-to field)*/
40+
int targetField() const;
41+
/**Returns the name of the target field (join-to field)*/
42+
QString targetFieldName() const;
43+
/**True if joined layer should be cached in virtual memory*/
44+
bool cacheInMemory() const;
45+
/**Returns true if user wants to create an attribute index on the join field*/
46+
bool createAttributeIndex() const;
47+
48+
private slots:
49+
void on_mJoinLayerComboBox_currentIndexChanged( int index );
50+
51+
private:
52+
/**Target layer*/
53+
QgsVectorLayer* mLayer;
54+
};
55+
56+
57+
#endif // QGSADDJOINDIALOG_H

‎src/app/qgsvectorlayerproperties.cpp

Lines changed: 73 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <limits>
2222

2323
#include "qgisapp.h"
24+
#include "qgsaddjoindialog.h"
2425
#include "qgsapplication.h"
2526
#include "qgsattributeactiondialog.h"
2627
#include "qgsapplydialog.h"
@@ -33,6 +34,7 @@
3334
#include "qgslabel.h"
3435
#include "qgsgenericprojectionselector.h"
3536
#include "qgslogger.h"
37+
#include "qgsmaplayerregistry.h"
3638
#include "qgspluginmetadata.h"
3739
#include "qgspluginregistry.h"
3840
#include "qgsproject.h"
@@ -133,6 +135,13 @@ QgsVectorLayerProperties::QgsVectorLayerProperties(
133135

134136
connect( sliderTransparency, SIGNAL( valueChanged( int ) ), this, SLOT( sliderTransparency_valueChanged( int ) ) );
135137

138+
//insert existing join info
139+
const QList< QgsVectorJoinInfo >& joins = layer->vectorJoins();
140+
for ( int i = 0; i < joins.size(); ++i )
141+
{
142+
addJoinToTreeWidget( joins[i] );
143+
}
144+
136145
//for each overlay plugin create a new tab
137146
int position;
138147
QList<QgsVectorOverlayPlugin*> overlayPluginList = overlayPlugins();
@@ -813,7 +822,6 @@ QString QgsVectorLayerProperties::metadata()
813822
{
814823
xMin = QString( "%1" ).arg( myExtent.xMinimum() );
815824
}
816-
817825
if ( qAbs( myExtent.yMinimum() ) > changeoverValue )
818826
{
819827
yMin = QString( "%1" ).arg( myExtent.yMinimum(), 0, 'f', 2 );
@@ -822,7 +830,6 @@ QString QgsVectorLayerProperties::metadata()
822830
{
823831
yMin = QString( "%1" ).arg( myExtent.yMinimum() );
824832
}
825-
826833
if ( qAbs( myExtent.xMaximum() ) > changeoverValue )
827834
{
828835
xMax = QString( "%1" ).arg( myExtent.xMaximum(), 0, 'f', 2 );
@@ -831,7 +838,6 @@ QString QgsVectorLayerProperties::metadata()
831838
{
832839
xMax = QString( "%1" ).arg( myExtent.xMaximum() );
833840
}
834-
835841
if ( qAbs( myExtent.yMaximum() ) > changeoverValue )
836842
{
837843
yMax = QString( "%1" ).arg( myExtent.yMaximum(), 0, 'f', 2 );
@@ -1171,6 +1177,70 @@ void QgsVectorLayerProperties::setUsingNewSymbology( bool useNewSymbology )
11711177
updateSymbologyPage();
11721178
}
11731179

1180+
void QgsVectorLayerProperties::on_mButtonAddJoin_clicked()
1181+
{
1182+
QgsAddJoinDialog d( layer );
1183+
if ( d.exec() == QDialog::Accepted )
1184+
{
1185+
QgsVectorJoinInfo info;
1186+
info.targetField = d.targetField();
1187+
info.joinLayerId = d.joinedLayerId();
1188+
info.joinField = d.joinField();
1189+
info.memoryCache = d.cacheInMemory();
1190+
if ( layer )
1191+
{
1192+
//create attribute index if possible. Todo: ask user if this should be done (e.g. in QgsAddJoinDialog)
1193+
if ( d.createAttributeIndex() )
1194+
{
1195+
QgsVectorLayer* joinLayer = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( info.joinLayerId ) );
1196+
if ( joinLayer )
1197+
{
1198+
joinLayer->dataProvider()->createAttributeIndex( info.joinField );
1199+
}
1200+
}
1201+
1202+
layer->addJoin( info );
1203+
loadRows(); //update attribute tab
1204+
addJoinToTreeWidget( info );
1205+
}
1206+
}
1207+
}
1208+
1209+
void QgsVectorLayerProperties::addJoinToTreeWidget( const QgsVectorJoinInfo& join )
1210+
{
1211+
QTreeWidgetItem* joinItem = new QTreeWidgetItem();
1212+
1213+
QgsVectorLayer* joinLayer = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( join.joinLayerId ) );
1214+
if ( !joinLayer )
1215+
{
1216+
return;
1217+
}
1218+
1219+
joinItem->setText( 0, joinLayer->name() );
1220+
joinItem->setData( 0, Qt::UserRole, join.joinLayerId );
1221+
QString joinFieldName = joinLayer->pendingFields().value( join.joinField ).name();
1222+
QString targetFieldName = layer->pendingFields().value( join.targetField ).name();
1223+
joinItem->setText( 1, joinFieldName );
1224+
joinItem->setData( 1, Qt::UserRole, join.joinField );
1225+
joinItem->setText( 2, targetFieldName );
1226+
joinItem->setData( 2, Qt::UserRole, join.targetField );
1227+
1228+
mJoinTreeWidget->addTopLevelItem( joinItem );
1229+
}
1230+
1231+
void QgsVectorLayerProperties::on_mButtonRemoveJoin_clicked()
1232+
{
1233+
QTreeWidgetItem* currentJoinItem = mJoinTreeWidget->currentItem();
1234+
if ( !layer || !currentJoinItem )
1235+
{
1236+
return;
1237+
}
1238+
1239+
layer->removeJoin( currentJoinItem->data( 0, Qt::UserRole ).toString() );
1240+
loadRows();
1241+
mJoinTreeWidget->takeTopLevelItem( mJoinTreeWidget->indexOfTopLevelItem( currentJoinItem ) );
1242+
}
1243+
11741244
void QgsVectorLayerProperties::useNewSymbology()
11751245
{
11761246
int res = QMessageBox::question( this, tr( "Symbology" ),

‎src/app/qgsvectorlayerproperties.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,9 @@ class QgsVectorLayerProperties : public QDialog, private Ui::QgsVectorLayerPrope
117117
void useNewSymbology();
118118
void setUsingNewSymbology( bool useNewSymbology );
119119

120+
void on_mButtonAddJoin_clicked();
121+
void on_mButtonRemoveJoin_clicked();
122+
120123
signals:
121124

122125
/** emitted when changes to layer were saved to update legend */
@@ -171,6 +174,9 @@ class QgsVectorLayerProperties : public QDialog, private Ui::QgsVectorLayerPrope
171174
/**Buffer pixmap which takes the picture of renderers before they are assigned to the vector layer*/
172175
//QPixmap bufferPixmap;
173176

177+
/**Adds a new join to mJoinTreeWidget*/
178+
void addJoinToTreeWidget( const QgsVectorJoinInfo& join );
179+
174180
static QMap< QgsVectorLayer::EditType, QString > editTypeMap;
175181
static void setupEditTypes();
176182
static QString editTypeButtonText( QgsVectorLayer::EditType type );

‎src/core/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ SET(QGIS_CORE_SRCS
8989
qgsvectordataprovider.cpp
9090
qgsvectorfilewriter.cpp
9191
qgsvectorlayer.cpp
92+
qgsvectorlayerjoinbuffer.cpp
9293
qgsvectorlayerundocommand.cpp
9394
qgsvectoroverlay.cpp
9495

‎src/core/qgsdataprovider.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ class CORE_EXPORT QgsDataProvider : public QObject
113113
* that can be used by the data provider to create a subset.
114114
* Must be implemented in the dataprovider.
115115
*/
116-
virtual bool setSubsetString( QString subset )
116+
virtual bool setSubsetString( QString subset, bool updateFeatureCount = true )
117117
{
118118
// NOP by default
119119
Q_UNUSED( subset );

‎src/core/qgsproject.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -675,6 +675,7 @@ QPair< bool, QList<QDomNode> > QgsProject::_getMapLayers( QDomDocument const &do
675675
bool returnStatus = true;
676676

677677
emit layerLoaded( 0, nl.count() );
678+
QList<QgsVectorLayer*> vLayerList; //collect
678679

679680
for ( int i = 0; i < nl.count(); i++ )
680681
{
@@ -714,6 +715,11 @@ QPair< bool, QList<QDomNode> > QgsProject::_getMapLayers( QDomDocument const &do
714715
if ( mapLayer->readXML( node ) )
715716
{
716717
mapLayer = QgsMapLayerRegistry::instance()->addMapLayer( mapLayer );
718+
QgsVectorLayer* vLayer = qobject_cast<QgsVectorLayer*>( mapLayer );
719+
if ( vLayer )
720+
{
721+
vLayerList.push_back( vLayer );
722+
}
717723
}
718724
else
719725
{
@@ -725,10 +731,18 @@ QPair< bool, QList<QDomNode> > QgsProject::_getMapLayers( QDomDocument const &do
725731

726732
brokenNodes.push_back( node );
727733
}
728-
729734
emit layerLoaded( i + 1, nl.count() );
730735
}
731736

737+
//Update field map of layers with joins and create join caches if necessary
738+
//Needs to be done here once all dependent layers are loaded
739+
QList<QgsVectorLayer*>::iterator vIt = vLayerList.begin();
740+
for ( ; vIt != vLayerList.end(); ++vIt )
741+
{
742+
( *vIt )->createJoinCaches();
743+
( *vIt )->updateFieldMap();
744+
}
745+
732746
return qMakePair( returnStatus, brokenNodes );
733747

734748
} // _getMapLayers

‎src/core/qgsvectordataprovider.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,11 @@ bool QgsVectorDataProvider::createSpatialIndex()
126126
return false;
127127
}
128128

129+
bool QgsVectorDataProvider::createAttributeIndex( int field )
130+
{
131+
return true;
132+
}
133+
129134
int QgsVectorDataProvider::capabilities() const
130135
{
131136
return QgsVectorDataProvider::NoCapabilities;

‎src/core/qgsvectordataprovider.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ class CORE_EXPORT QgsVectorDataProvider : public QgsDataProvider
7474
RandomSelectGeometryAtId = 1 << 10,
7575
/** DEPRECATED - do not use */
7676
SequentialSelectGeometryAtId = 1 << 11,
77+
CreateAttributeIndex = 1 << 12
7778
};
7879

7980
/** bitmask of all provider's editing capabilities */
@@ -272,6 +273,9 @@ class CORE_EXPORT QgsVectorDataProvider : public QgsDataProvider
272273
*/
273274
virtual bool createSpatialIndex();
274275

276+
/**Create an attribute index on the datasource*/
277+
virtual bool createAttributeIndex( int field );
278+
275279
/** Returns a bitmask containing the supported capabilities
276280
Note, some capabilities may change depending on whether
277281
a spatial filter is active on this provider, so it may

‎src/core/qgsvectorlayer.cpp

Lines changed: 263 additions & 34 deletions
Large diffs are not rendered by default.

‎src/core/qgsvectorlayer.h

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "qgssnapper.h"
3131
#include "qgsfield.h"
3232

33+
3334
class QPainter;
3435
class QImage;
3536

@@ -45,15 +46,37 @@ class QgsUndoCommand;
4546
class QgsVectorDataProvider;
4647
class QgsVectorOverlay;
4748
class QgsSingleSymbolRendererV2;
48-
4949
class QgsRectangle;
50+
class QgsVectorLayerJoinBuffer;
5051

5152
class QgsFeatureRendererV2;
5253

5354
typedef QList<int> QgsAttributeList;
5455
typedef QSet<int> QgsFeatureIds;
5556
typedef QSet<int> QgsAttributeIds;
5657

58+
struct CORE_EXPORT QgsVectorJoinInfo
59+
{
60+
/**Join field in the target layer*/
61+
int targetField;
62+
/**Source layer*/
63+
QString joinLayerId;
64+
/**Join field in the source layer*/
65+
int joinField;
66+
/**True if the join is cached in virtual memory*/
67+
bool memoryCache;
68+
/**Cache for joined attributes to provide fast lookup (size is 0 if no memory caching)*/
69+
QHash< QString, QgsAttributeMap> cachedAttributes;
70+
};
71+
72+
/**Join information prepared for fast attribute id mapping in QgsVectorLayerJoinBuffer::updateFeatureAttributes().
73+
Created in the select() method of QgsVectorLayerJoinBuffer for the joins that contain fetched attributes*/
74+
struct CORE_EXPORT QgsFetchJoinInfo
75+
{
76+
const QgsVectorJoinInfo* joinInfo;
77+
QgsAttributeList attributes; //attributes to fetch
78+
int indexOffset; //index offset between this layer and join layer
79+
};
5780

5881
/** \ingroup core
5982
* Vector layer backed by a data source provider.
@@ -127,6 +150,16 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer
127150
/** Setup the coordinate system tranformation for the layer */
128151
void setCoordinateSystem();
129152

153+
/**Joins another vector layer to this layer
154+
@param joinInfo join object containing join layer id, target and source field
155+
@param cacheInMemory if true: caches the content of the join layer in virtual memory*/
156+
void addJoin( QgsVectorJoinInfo joinInfo );
157+
158+
/**Removes a vector layer join*/
159+
void removeJoin( const QString& joinLayerId );
160+
161+
const QList< QgsVectorJoinInfo >& vectorJoins() const;
162+
130163
/** Get the label object associated with this layer */
131164
QgsLabel *label();
132165

@@ -594,6 +627,14 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer
594627
@note public and static from version 1.4 */
595628
static void drawVertexMarker( double x, double y, QPainter& p, QgsVectorLayer::VertexMarkerType type, int vertexSize );
596629

630+
/**Assembles mUpdatedFields considering provider fields, joined fields and added fields
631+
@note added in version 1.6*/
632+
void updateFieldMap();
633+
634+
/**Caches joined attributes if required (and not already done)*/
635+
void createJoinCaches();
636+
637+
597638
public slots:
598639
/** Select feature by its ID, optionally emit signal selectionChanged() */
599640
void select( int featureId, bool emitSignal = true );
@@ -611,6 +652,9 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer
611652
*/
612653
virtual void updateExtents();
613654

655+
/**Check if there is a join with a layer that will be removed*/
656+
void checkJoinLayerRemove( QString theLayerId );
657+
614658
signals:
615659

616660
/** This signal is emited when selection was changed */
@@ -701,9 +745,19 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer
701745
/**Reads vertex marker size from settings*/
702746
static int currentVertexMarkerSize();
703747

704-
/**Update feature with uncommited attribute updates*/
748+
/**Update feature with uncommited attribute updates and joined attributes*/
705749
void updateFeatureAttributes( QgsFeature &f, bool all = false );
706750

751+
/**Adds joined attributes to a feature
752+
@param f the feature to add the attributes
753+
@param joinInfo vector join
754+
@param joinFieldName name of the (source) join Field
755+
@param joinValue lookup value for join
756+
@param attributes (join layer) attribute indices to add
757+
@param attributeOffset index offset to get from join layer attribute index to layer index*/
758+
void addJoinedFeatureAttributes( QgsFeature& f, const QgsVectorJoinInfo& joinInfo, const QString& joinFieldName, const QVariant& joinValue,
759+
const QgsAttributeList& attributes, int attributeIndexOffset );
760+
707761
/**Update feature with uncommited geometry updates*/
708762
void updateFeatureGeometry( QgsFeature &f );
709763

@@ -722,6 +776,13 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer
722776
/** Stop version 2 renderer and selected renderer (if required) */
723777
void stopRendererV2( QgsRenderContext& rendererContext, QgsSingleSymbolRendererV2* selRenderer );
724778

779+
/** Helper function to find out the maximum index of a field map
780+
@return true in case of success, otherwise false (e.g. empty map)*/
781+
bool maxIndex( const QgsFieldMap& fMap, int& index ) const;
782+
783+
/**Updates an index in an attribute map to a new value (usually necessary because of a join operation)*/
784+
void updateAttributeMapIndex( QgsAttributeMap& map, int oldIndex, int newIndex ) const;
785+
725786
private: // Private attributes
726787

727788
/** Update threshold for drawing features as they are read. A value of zero indicates
@@ -846,6 +907,9 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer
846907
QSet<int> mFetchConsidered;
847908
QgsGeometryMap::iterator mFetchChangedGeomIt;
848909
QgsFeatureList::iterator mFetchAddedFeaturesIt;
910+
911+
//stores information about joined layers
912+
QgsVectorLayerJoinBuffer* mJoinBuffer;
849913
};
850914

851915
#endif

‎src/core/qgsvectorlayerjoinbuffer.cpp

Lines changed: 346 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,346 @@
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+
18+
#include "qgsvectorlayerjoinbuffer.h"
19+
#include "qgsmaplayerregistry.h"
20+
#include "qgsvectordataprovider.h"
21+
22+
#include <QDomElement>
23+
24+
QgsVectorLayerJoinBuffer::QgsVectorLayerJoinBuffer()
25+
{
26+
}
27+
28+
QgsVectorLayerJoinBuffer::~QgsVectorLayerJoinBuffer()
29+
{
30+
}
31+
32+
void QgsVectorLayerJoinBuffer::addJoin( QgsVectorJoinInfo joinInfo )
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+
return;
51+
}
52+
}
53+
}
54+
55+
void QgsVectorLayerJoinBuffer::cacheJoinLayer( QgsVectorJoinInfo& joinInfo )
56+
{
57+
//memory cache not required or already done
58+
if ( !joinInfo.memoryCache || joinInfo.cachedAttributes.size() > 0 )
59+
{
60+
return;
61+
}
62+
63+
QgsVectorLayer* cacheLayer = dynamic_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( joinInfo.joinLayerId ) );
64+
if ( cacheLayer )
65+
{
66+
joinInfo.cachedAttributes.clear();
67+
cacheLayer->select( cacheLayer->pendingAllAttributesList(), QgsRectangle(), false, false );
68+
QgsFeature f;
69+
while ( cacheLayer->nextFeature( f ) )
70+
{
71+
const QgsAttributeMap& map = f.attributeMap();
72+
joinInfo.cachedAttributes.insert( map.value( joinInfo.joinField ).toString(), map );
73+
}
74+
}
75+
}
76+
77+
void QgsVectorLayerJoinBuffer::updateFieldMap( QgsFieldMap& fields, int& maxIndex )
78+
{
79+
int currentMaxIndex = 0; //maximum index of the current join layer
80+
81+
QList< QgsVectorJoinInfo>::const_iterator joinIt = mVectorJoins.constBegin();
82+
for ( ; joinIt != mVectorJoins.constEnd(); ++joinIt )
83+
{
84+
QgsVectorLayer* joinLayer = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( joinIt->joinLayerId ) );
85+
if ( !joinLayer )
86+
{
87+
continue;
88+
}
89+
90+
const QgsFieldMap& joinFields = joinLayer->pendingFields();
91+
QgsFieldMap::const_iterator fieldIt = joinFields.constBegin();
92+
for ( ; fieldIt != joinFields.constEnd(); ++fieldIt )
93+
{
94+
fields.insert( maxIndex + 1 + fieldIt.key(), fieldIt.value() );
95+
}
96+
97+
if ( maximumIndex( joinFields, currentMaxIndex ) )
98+
{
99+
maxIndex += ( currentMaxIndex + 1 ); //+1 because there are fields with index 0
100+
}
101+
}
102+
}
103+
104+
void QgsVectorLayerJoinBuffer::createJoinCaches()
105+
{
106+
QList< QgsVectorJoinInfo >::iterator joinIt = mVectorJoins.begin();
107+
for ( ; joinIt != mVectorJoins.end(); ++joinIt )
108+
{
109+
cacheJoinLayer( *joinIt );
110+
}
111+
}
112+
113+
void QgsVectorLayerJoinBuffer::select( const QgsAttributeList& fetchAttributes,
114+
QgsAttributeList& sourceJoinFields, int maxProviderIndex )
115+
{
116+
mFetchJoinInfos.clear();
117+
sourceJoinFields.clear();
118+
119+
QgsAttributeList::const_iterator attIt = fetchAttributes.constBegin();
120+
for ( ; attIt != fetchAttributes.constEnd(); ++attIt )
121+
{
122+
int indexOffset;
123+
const QgsVectorJoinInfo* joinInfo = joinForFieldIndex( *attIt, maxProviderIndex, indexOffset );
124+
if ( joinInfo )
125+
{
126+
QgsVectorLayer* joinLayer = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( joinInfo->joinLayerId ) );
127+
if ( joinLayer )
128+
{
129+
mFetchJoinInfos[ joinLayer ].joinInfo = joinInfo;
130+
mFetchJoinInfos[ joinLayer].attributes.push_back( *attIt - indexOffset ); //store provider index
131+
mFetchJoinInfos[ joinLayer ].indexOffset = indexOffset;
132+
//for joined fields, we always need to request the targetField from the provider too
133+
if ( !fetchAttributes.contains( joinInfo->targetField ) )
134+
{
135+
sourceJoinFields << joinInfo->targetField;
136+
}
137+
}
138+
}
139+
}
140+
}
141+
142+
void QgsVectorLayerJoinBuffer::updateFeatureAttributes( QgsFeature &f, int maxProviderIndex, bool all )
143+
{
144+
if ( all )
145+
{
146+
int index = maxProviderIndex + 1;
147+
int currentMaxIndex;
148+
149+
QList< QgsVectorJoinInfo >::const_iterator joinIt = mVectorJoins.constBegin();
150+
for ( ; joinIt != mVectorJoins.constEnd(); ++joinIt )
151+
{
152+
QgsVectorLayer* joinLayer = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( joinIt->joinLayerId ) );
153+
if ( !joinLayer )
154+
{
155+
continue;
156+
}
157+
158+
QString joinFieldName = joinLayer->pendingFields().value( joinIt->joinField ).name();
159+
if ( joinFieldName.isEmpty() )
160+
{
161+
continue;
162+
}
163+
164+
QVariant targetFieldValue = f.attributeMap().value( joinIt->targetField );
165+
if ( !targetFieldValue.isValid() )
166+
{
167+
continue;
168+
}
169+
170+
addJoinedFeatureAttributes( f, *joinIt, joinFieldName, targetFieldValue, joinLayer->pendingAllAttributesList(), index );
171+
172+
maximumIndex( joinLayer->pendingFields(), currentMaxIndex );
173+
index += ( currentMaxIndex + 1 );
174+
}
175+
}
176+
else
177+
{
178+
QMap<QgsVectorLayer*, QgsFetchJoinInfo>::const_iterator joinIt = mFetchJoinInfos.constBegin();
179+
for ( ; joinIt != mFetchJoinInfos.constEnd(); ++joinIt )
180+
{
181+
QgsVectorLayer* joinLayer = joinIt.key();
182+
if ( !joinLayer )
183+
{
184+
continue;
185+
}
186+
187+
QString joinFieldName = joinLayer->pendingFields().value( joinIt.value().joinInfo->joinField ).name();
188+
if ( joinFieldName.isEmpty() )
189+
{
190+
continue;
191+
}
192+
193+
QVariant targetFieldValue = f.attributeMap().value( joinIt->joinInfo->targetField );
194+
if ( !targetFieldValue.isValid() )
195+
{
196+
continue;
197+
}
198+
199+
addJoinedFeatureAttributes( f, *( joinIt.value().joinInfo ), joinFieldName, targetFieldValue, joinIt.value().attributes, joinIt.value().indexOffset );
200+
}
201+
}
202+
}
203+
204+
void QgsVectorLayerJoinBuffer::addJoinedFeatureAttributes( QgsFeature& f, const QgsVectorJoinInfo& joinInfo, const QString& joinFieldName,
205+
const QVariant& joinValue, const QgsAttributeList& attributes, int attributeIndexOffset )
206+
{
207+
const QHash< QString, QgsAttributeMap>& memoryCache = joinInfo.cachedAttributes;
208+
if ( !memoryCache.isEmpty() ) //use join memory cache
209+
{
210+
QgsAttributeMap featureAttributes = memoryCache.value( joinValue.toString() );
211+
bool found = !featureAttributes.isEmpty();
212+
QgsAttributeList::const_iterator attIt = attributes.constBegin();
213+
for ( ; attIt != attributes.constEnd(); ++attIt )
214+
{
215+
if ( found )
216+
{
217+
f.addAttribute( *attIt + attributeIndexOffset, featureAttributes.value( *attIt ) );
218+
}
219+
else
220+
{
221+
f.addAttribute( *attIt + attributeIndexOffset, QVariant() );
222+
}
223+
}
224+
}
225+
else //work with subset string
226+
{
227+
QgsVectorLayer* joinLayer = dynamic_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( joinInfo.joinLayerId ) );
228+
if ( !joinLayer )
229+
{
230+
return;
231+
}
232+
233+
//no memory cache, query the joined values by setting substring
234+
QString subsetString = joinLayer->dataProvider()->subsetString(); //provider might already have a subset string
235+
QString bkSubsetString = subsetString;
236+
if ( !subsetString.isEmpty() )
237+
{
238+
subsetString.append( " AND " );
239+
}
240+
241+
subsetString.append( "\"" + joinFieldName + "\"" + " = " + "\"" + joinValue.toString() + "\"" );
242+
joinLayer->dataProvider()->setSubsetString( subsetString, false );
243+
244+
//select (no geometry)
245+
joinLayer->select( attributes, QgsRectangle(), false, false );
246+
247+
//get first feature
248+
QgsFeature fet;
249+
if ( joinLayer->nextFeature( fet ) )
250+
{
251+
QgsAttributeMap attMap = fet.attributeMap();
252+
QgsAttributeMap::const_iterator attIt = attMap.constBegin();
253+
for ( ; attIt != attMap.constEnd(); ++attIt )
254+
{
255+
f.addAttribute( attIt.key() + attributeIndexOffset, attIt.value() );
256+
}
257+
}
258+
else //no suitable join feature found, insert invalid variants
259+
{
260+
QgsAttributeList::const_iterator attIt = attributes.constBegin();
261+
for ( ; attIt != attributes.constEnd(); ++attIt )
262+
{
263+
f.addAttribute( *attIt + attributeIndexOffset, QVariant() );
264+
}
265+
}
266+
267+
joinLayer->dataProvider()->setSubsetString( bkSubsetString, false );
268+
}
269+
}
270+
271+
void QgsVectorLayerJoinBuffer::writeXml( QDomNode& layer_node, QDomDocument& document ) const
272+
{
273+
QDomElement vectorJoinsElem = document.createElement( "vectorjoins" );
274+
layer_node.appendChild( vectorJoinsElem );
275+
QList< QgsVectorJoinInfo >::const_iterator joinIt = mVectorJoins.constBegin();
276+
for ( ; joinIt != mVectorJoins.constEnd(); ++joinIt )
277+
{
278+
QDomElement joinElem = document.createElement( "join" );
279+
joinElem.setAttribute( "targetField", joinIt->targetField );
280+
joinElem.setAttribute( "joinLayerId", joinIt->joinLayerId );
281+
joinElem.setAttribute( "joinField", joinIt->joinField );
282+
joinElem.setAttribute( "memoryCache", !joinIt->cachedAttributes.isEmpty() );
283+
vectorJoinsElem.appendChild( joinElem );
284+
}
285+
}
286+
287+
void QgsVectorLayerJoinBuffer::readXml( QDomNode& layer_node )
288+
{
289+
mVectorJoins.clear();
290+
QDomElement vectorJoinsElem = layer_node.firstChildElement( "vectorjoins" );
291+
if ( !vectorJoinsElem.isNull() )
292+
{
293+
QDomNodeList joinList = vectorJoinsElem.elementsByTagName( "join" );
294+
for ( int i = 0; i < joinList.size(); ++i )
295+
{
296+
QDomElement infoElem = joinList.at( i ).toElement();
297+
QgsVectorJoinInfo info;
298+
info.joinField = infoElem.attribute( "joinField" ).toInt();
299+
info.joinLayerId = infoElem.attribute( "joinLayerId" );
300+
info.targetField = infoElem.attribute( "targetField" ).toInt();
301+
info.memoryCache = infoElem.attribute( "memoryCache" ).toInt();
302+
addJoin( info );
303+
}
304+
}
305+
}
306+
307+
const QgsVectorJoinInfo* QgsVectorLayerJoinBuffer::joinForFieldIndex( int index, int maxProviderIndex, int& indexOffset ) const
308+
{
309+
int currentMaxIndex = 0;
310+
int totIndex = maxProviderIndex + 1;
311+
312+
//go through all the joins to search the index
313+
QList< QgsVectorJoinInfo>::const_iterator joinIt = mVectorJoins.constBegin();
314+
for ( ; joinIt != mVectorJoins.constEnd(); ++joinIt )
315+
{
316+
QgsVectorLayer* joinLayer = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( joinIt->joinLayerId ) );
317+
if ( !joinLayer )
318+
{
319+
continue;
320+
}
321+
322+
if ( joinLayer->pendingFields().contains( index - totIndex ) )
323+
{
324+
indexOffset = totIndex;
325+
return &( *joinIt );
326+
}
327+
328+
maximumIndex( joinLayer->pendingFields(), currentMaxIndex );
329+
totIndex += ( currentMaxIndex + 1 );
330+
}
331+
332+
//an added field or a provider field
333+
return 0;
334+
}
335+
336+
bool QgsVectorLayerJoinBuffer::maximumIndex( const QgsFieldMap& fMap, int& index )
337+
{
338+
if ( fMap.size() < 1 )
339+
{
340+
return false;
341+
}
342+
QgsFieldMap::const_iterator endIt = fMap.constEnd();
343+
--endIt;
344+
index = endIt.key();
345+
return true;
346+
}

‎src/core/qgsvectorlayerjoinbuffer.h

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/***************************************************************************
2+
qgsvectorlayerjoinbuffer.h
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+
18+
#ifndef QGSVECTORLAYERJOINBUFFER_H
19+
#define QGSVECTORLAYERJOINBUFFER_H
20+
21+
#include "qgsfeature.h"
22+
#include "qgsvectorlayer.h"
23+
24+
#include <QHash>
25+
#include <QString>
26+
27+
/**Manages joined field for a vector layer*/
28+
class CORE_EXPORT QgsVectorLayerJoinBuffer
29+
{
30+
public:
31+
QgsVectorLayerJoinBuffer();
32+
~QgsVectorLayerJoinBuffer();
33+
34+
/**Joins another vector layer to this layer
35+
@param joinInfo join object containing join layer id, target and source field
36+
@param cacheInMemory if true: caches the content of the join layer in virtual memory*/
37+
void addJoin( QgsVectorJoinInfo joinInfo );
38+
39+
/**Removes a vector layer join*/
40+
void removeJoin( const QString& joinLayerId );
41+
42+
/**Creates QgsVectorLayerJoinBuffer for the joins containing attributes to fetch*/
43+
void select( const QgsAttributeList& fetchAttributes,
44+
QgsAttributeList& sourceJoinFields, int maxProviderIndex );
45+
46+
/**Updates field map with joined attributes
47+
@param fields map to append joined attributes
48+
@param maxIndex in/out: maximum attribute index*/
49+
void updateFieldMap( QgsFieldMap& fields, int& maxIndex );
50+
51+
/**Update feature with uncommited attribute updates and joined attributes*/
52+
void updateFeatureAttributes( QgsFeature &f, int maxProviderIndex, bool all = false );
53+
54+
/**Calls cacheJoinLayer() for all vector joins*/
55+
void createJoinCaches();
56+
57+
/**Saves mVectorJoins to xml under the layer node*/
58+
void writeXml( QDomNode& layer_node, QDomDocument& document ) const;
59+
60+
/**Reads joins from project file*/
61+
void readXml( QDomNode& layer_node );
62+
63+
/**Quick way to test if there is any join at all*/
64+
bool containsJoins() const { return ( mVectorJoins.size() > 0 ); }
65+
/**Quick way to test if there is a join to be fetched*/
66+
bool containsFetchJoins() const { return ( mFetchJoinInfos.size() > 0 ); }
67+
68+
const QList< QgsVectorJoinInfo >& vectorJoins() const { return mVectorJoins; }
69+
70+
/** Helper function to find out the maximum index of a field map
71+
@return true in case of success, otherwise false (e.g. empty map)*/
72+
static bool maximumIndex( const QgsFieldMap& fMap, int& index );
73+
74+
private:
75+
76+
/**Joined vector layers*/
77+
QList< QgsVectorJoinInfo > mVectorJoins;
78+
79+
/**Informations about joins used in the current select() statement.
80+
Allows faster mapping of attribute ids compared to mVectorJoins*/
81+
QMap<QgsVectorLayer*, QgsFetchJoinInfo> mFetchJoinInfos;
82+
83+
/**Caches attributes of join layer in memory if QgsVectorJoinInfo.memoryCache is true (and the cache is not already there)*/
84+
void cacheJoinLayer( QgsVectorJoinInfo& joinInfo );
85+
86+
/**Adds joined attributes to a feature
87+
@param f the feature to add the attributes
88+
@param joinInfo vector join
89+
@param joinFieldName name of the (source) join Field
90+
@param joinValue lookup value for join
91+
@param attributes (join layer) attribute indices to add
92+
@param attributeOffset index offset to get from join layer attribute index to layer index*/
93+
void addJoinedFeatureAttributes( QgsFeature& f, const QgsVectorJoinInfo& joinInfo, const QString& joinFieldName, const QVariant& joinValue,
94+
const QgsAttributeList& attributes, int attributeIndexOffset );
95+
96+
/**Finds the vector join for a layer field index.
97+
@param index this layers attribute index
98+
@param maxProviderIndex maximum attribute index of the vectorlayer provider
99+
@param indexOffset out: offset between layer index and original provider index
100+
@return pointer to the join if the index belongs to a joined field, otherwise 0 (possibily provider field or added field)*/
101+
const QgsVectorJoinInfo* joinForFieldIndex( int index, int maxProviderIndex, int& indexOffset ) const;
102+
};
103+
104+
#endif // QGSVECTORLAYERJOINBUFFER_H

‎src/core/symbology-ng/qgssinglesymbolrendererv2.cpp

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ QgsSymbolV2* QgsSingleSymbolRendererV2::symbolForFeature( QgsFeature& feature )
6565

6666
void QgsSingleSymbolRendererV2::startRender( QgsRenderContext& context, const QgsVectorLayer *vlayer )
6767
{
68+
if ( !mSymbol )
69+
{
70+
return;
71+
}
6872
mRotationFieldIdx = mRotationField.isEmpty() ? -1 : vlayer->fieldNameIndex( mRotationField );
6973
mSizeScaleFieldIdx = mSizeScaleField.isEmpty() ? -1 : vlayer->fieldNameIndex( mSizeScaleField );
7074

@@ -99,6 +103,10 @@ void QgsSingleSymbolRendererV2::startRender( QgsRenderContext& context, const Qg
99103

100104
void QgsSingleSymbolRendererV2::stopRender( QgsRenderContext& context )
101105
{
106+
if ( !mSymbol )
107+
{
108+
return;
109+
}
102110
mSymbol->stopRender( context );
103111

104112
if ( mRotationFieldIdx != -1 || mSizeScaleFieldIdx != -1 )
@@ -134,7 +142,14 @@ void QgsSingleSymbolRendererV2::setSymbol( QgsSymbolV2* s )
134142

135143
QString QgsSingleSymbolRendererV2::dump()
136144
{
137-
return QString( "SINGLE: %1" ).arg( mSymbol->dump() );
145+
if ( mSymbol )
146+
{
147+
return QString( "SINGLE: %1" ).arg( mSymbol->dump() );
148+
}
149+
else
150+
{
151+
return "";
152+
}
138153
}
139154

140155
QgsFeatureRendererV2* QgsSingleSymbolRendererV2::clone()
@@ -205,10 +220,12 @@ QDomElement QgsSingleSymbolRendererV2::save( QDomDocument& doc )
205220

206221
QgsLegendSymbologyList QgsSingleSymbolRendererV2::legendSymbologyItems( QSize iconSize )
207222
{
208-
QPixmap pix = QgsSymbolLayerV2Utils::symbolPreviewPixmap( mSymbol, iconSize );
209-
210223
QgsLegendSymbologyList lst;
211-
lst << qMakePair( QString(), pix );
224+
if ( mSymbol )
225+
{
226+
QPixmap pix = QgsSymbolLayerV2Utils::symbolPreviewPixmap( mSymbol, iconSize );
227+
lst << qMakePair( QString(), pix );
228+
}
212229
return lst;
213230
}
214231

‎src/providers/ogr/qgsogrprovider.cpp

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ QgsOgrProvider::~QgsOgrProvider()
229229
}
230230
}
231231

232-
bool QgsOgrProvider::setSubsetString( QString theSQL )
232+
bool QgsOgrProvider::setSubsetString( QString theSQL, bool updateFeatureCount )
233233
{
234234
QgsCPLErrorHandler handler;
235235

@@ -287,7 +287,10 @@ bool QgsOgrProvider::setSubsetString( QString theSQL )
287287

288288
// getting the total number of features in the layer
289289
// TODO: This can be expensive, do we really need it!
290-
recalculateFeatureCount();
290+
if ( updateFeatureCount )
291+
{
292+
recalculateFeatureCount();
293+
}
291294

292295
// check the validity of the layer
293296
QgsDebugMsg( "checking validity" );
@@ -1046,6 +1049,20 @@ bool QgsOgrProvider::createSpatialIndex()
10461049
return indexfile.exists();
10471050
}
10481051

1052+
bool QgsOgrProvider::createAttributeIndex( int field )
1053+
{
1054+
QString layerName = OGR_FD_GetName( OGR_L_GetLayerDefn( ogrOrigLayer ) );
1055+
QString dropSql = QString( "DROP INDEX ON %1" ).arg( quotedIdentifier( layerName ) );
1056+
QString createSql = QString( "CREATE INDEX ON %1 USING %2" ).arg( quotedIdentifier( layerName ) ).arg( fields()[field].name() );
1057+
OGR_DS_ExecuteSQL( ogrDataSource, mEncoding->fromUnicode( dropSql ).data(), OGR_L_GetSpatialFilter( ogrOrigLayer ), "SQL" );
1058+
OGR_DS_ExecuteSQL( ogrDataSource, mEncoding->fromUnicode( createSql ).data(), OGR_L_GetSpatialFilter( ogrOrigLayer ), "SQL" );
1059+
1060+
QFileInfo fi( mFilePath ); // to get the base name
1061+
//find out, if the .idm file is there
1062+
QFile indexfile( fi.path().append( "/" ).append( fi.completeBaseName() ).append( ".idm" ) );
1063+
return indexfile.exists();
1064+
}
1065+
10491066
bool QgsOgrProvider::deleteFeatures( const QgsFeatureIds & id )
10501067
{
10511068
QgsCPLErrorHandler handler;
@@ -1182,6 +1199,7 @@ int QgsOgrProvider::capabilities() const
11821199
// adding attributes was added in GDAL 1.6
11831200
ability |= AddAttributes;
11841201
#endif
1202+
ability |= CreateAttributeIndex;
11851203

11861204
if ( mAttributeFields.size() == 0 )
11871205
{
@@ -1816,7 +1834,14 @@ QgsCoordinateReferenceSystem QgsOgrProvider::crs()
18161834

18171835
void QgsOgrProvider::uniqueValues( int index, QList<QVariant> &uniqueValues, int limit )
18181836
{
1819-
QgsField fld = mAttributeFields[index];
1837+
uniqueValues.clear();
1838+
1839+
QgsField fld = mAttributeFields.value( index );
1840+
if ( fld.name().isNull() )
1841+
{
1842+
return; //not a provider field
1843+
}
1844+
18201845
QString theLayerName = OGR_FD_GetName( OGR_L_GetLayerDefn( ogrLayer ) );
18211846

18221847
QString sql = QString( "SELECT DISTINCT %1 FROM %2" )
@@ -1830,8 +1855,6 @@ void QgsOgrProvider::uniqueValues( int index, QList<QVariant> &uniqueValues, int
18301855

18311856
sql += QString( " ORDER BY %1" ).arg( fld.name() );
18321857

1833-
uniqueValues.clear();
1834-
18351858
QgsDebugMsg( QString( "SQL: %1" ).arg( sql ) );
18361859
OGRLayerH l = OGR_DS_ExecuteSQL( ogrDataSource, mEncoding->fromUnicode( sql ).data(), NULL, "SQL" );
18371860
if ( l == 0 )

‎src/providers/ogr/qgsogrprovider.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ class QgsOgrProvider : public QgsVectorDataProvider
9898
virtual bool supportsSubsetString() { return true; }
9999

100100
/** mutator for sql where clause used to limit dataset size */
101-
virtual bool setSubsetString( QString theSQL );
101+
virtual bool setSubsetString( QString theSQL, bool updateFeatureCount = true );
102102

103103
/**
104104
* Get feature type.
@@ -155,6 +155,9 @@ class QgsOgrProvider : public QgsVectorDataProvider
155155
@return true in case of success*/
156156
virtual bool createSpatialIndex();
157157

158+
/**Create an attribute index on the datasource*/
159+
virtual bool createAttributeIndex( int field );
160+
158161
/** Returns a bitmask containing the supported capabilities
159162
Note, some capabilities may change depending on whether
160163
a spatial filter is active on this provider, so it may

‎src/providers/postgres/qgspostgresprovider.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2793,7 +2793,7 @@ int QgsPostgresProvider::capabilities() const
27932793
return enabledCapabilities;
27942794
}
27952795

2796-
bool QgsPostgresProvider::setSubsetString( QString theSQL )
2796+
bool QgsPostgresProvider::setSubsetString( QString theSQL, bool updateFeatureCount )
27972797
{
27982798
QString prevWhere = sqlWhereClause;
27992799

@@ -2828,7 +2828,10 @@ bool QgsPostgresProvider::setSubsetString( QString theSQL )
28282828
// uri? Perhaps this needs some rationalisation.....
28292829
setDataSourceUri( mUri.uri() );
28302830

2831-
featuresCounted = -1;
2831+
if ( updateFeatureCount )
2832+
{
2833+
featuresCounted = -1;
2834+
}
28322835
layerExtent.setMinimal();
28332836

28342837
return true;

‎src/providers/postgres/qgspostgresprovider.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ class QgsPostgresProvider : public QgsVectorDataProvider
250250
QString subsetString();
251251

252252
/** mutator for sql where clause used to limit dataset size */
253-
bool setSubsetString( QString theSQL );
253+
bool setSubsetString( QString theSQL, bool updateFeatureCount = true );
254254

255255
virtual bool supportsSubsetString() { return true; }
256256

‎src/providers/spatialite/qgsspatialiteprovider.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -560,7 +560,7 @@ QString QgsSpatiaLiteProvider::subsetString()
560560
return mSubsetString;
561561
}
562562

563-
bool QgsSpatiaLiteProvider::setSubsetString( QString theSQL )
563+
bool QgsSpatiaLiteProvider::setSubsetString( QString theSQL, bool updateFeatureCount )
564564
{
565565
QString prevSubsetString = mSubsetString;
566566
mSubsetString = theSQL;
@@ -571,8 +571,10 @@ bool QgsSpatiaLiteProvider::setSubsetString( QString theSQL )
571571
setDataSourceUri( uri.uri() );
572572

573573
// update feature count and extents
574-
if ( getTableSummary() )
574+
if ( updateFeatureCount && getTableSummary() )
575+
{
575576
return true;
577+
}
576578

577579
mSubsetString = prevSubsetString;
578580

‎src/providers/spatialite/qgsspatialiteprovider.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ class QgsSpatiaLiteProvider: public QgsVectorDataProvider
8080
virtual QString subsetString();
8181

8282
/** mutator for sql where clause used to limit dataset size */
83-
virtual bool setSubsetString( QString theSQL );
83+
virtual bool setSubsetString( QString theSQL, bool updateFeatureCount = true );
8484

8585
virtual bool supportsSubsetString() { return true; }
8686

‎src/ui/qgsaddjoindialogbase.ui

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<ui version="4.0">
3+
<class>QgsAddJoinDialogBase</class>
4+
<widget class="QDialog" name="QgsAddJoinDialogBase">
5+
<property name="geometry">
6+
<rect>
7+
<x>0</x>
8+
<y>0</y>
9+
<width>281</width>
10+
<height>160</height>
11+
</rect>
12+
</property>
13+
<property name="windowTitle">
14+
<string>Add vector join</string>
15+
</property>
16+
<layout class="QGridLayout" name="gridLayout">
17+
<item row="0" column="0">
18+
<widget class="QLabel" name="mJoinLayerLabel">
19+
<property name="text">
20+
<string>Join layer: </string>
21+
</property>
22+
</widget>
23+
</item>
24+
<item row="0" column="1">
25+
<widget class="QComboBox" name="mJoinLayerComboBox"/>
26+
</item>
27+
<item row="1" column="0">
28+
<widget class="QLabel" name="mJoinFieldLabel">
29+
<property name="text">
30+
<string>Join field:</string>
31+
</property>
32+
</widget>
33+
</item>
34+
<item row="1" column="1">
35+
<widget class="QComboBox" name="mJoinFieldComboBox"/>
36+
</item>
37+
<item row="2" column="0">
38+
<widget class="QLabel" name="mTargetFieldLabel">
39+
<property name="text">
40+
<string>Target field:</string>
41+
</property>
42+
</widget>
43+
</item>
44+
<item row="2" column="1">
45+
<widget class="QComboBox" name="mTargetFieldComboBox"/>
46+
</item>
47+
<item row="5" column="0" colspan="2">
48+
<widget class="QDialogButtonBox" name="buttonBox">
49+
<property name="orientation">
50+
<enum>Qt::Horizontal</enum>
51+
</property>
52+
<property name="standardButtons">
53+
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
54+
</property>
55+
</widget>
56+
</item>
57+
<item row="4" column="0" colspan="2">
58+
<widget class="QCheckBox" name="mCreateIndexCheckBox">
59+
<property name="text">
60+
<string>Create attribute index on join field</string>
61+
</property>
62+
</widget>
63+
</item>
64+
<item row="3" column="0" colspan="2">
65+
<widget class="QCheckBox" name="mCacheInMemoryCheckBox">
66+
<property name="text">
67+
<string>Cache join layer in virtual memory</string>
68+
</property>
69+
</widget>
70+
</item>
71+
</layout>
72+
</widget>
73+
<resources/>
74+
<connections>
75+
<connection>
76+
<sender>buttonBox</sender>
77+
<signal>accepted()</signal>
78+
<receiver>QgsAddJoinDialogBase</receiver>
79+
<slot>accept()</slot>
80+
<hints>
81+
<hint type="sourcelabel">
82+
<x>248</x>
83+
<y>254</y>
84+
</hint>
85+
<hint type="destinationlabel">
86+
<x>157</x>
87+
<y>274</y>
88+
</hint>
89+
</hints>
90+
</connection>
91+
<connection>
92+
<sender>buttonBox</sender>
93+
<signal>rejected()</signal>
94+
<receiver>QgsAddJoinDialogBase</receiver>
95+
<slot>reject()</slot>
96+
<hints>
97+
<hint type="sourcelabel">
98+
<x>316</x>
99+
<y>260</y>
100+
</hint>
101+
<hint type="destinationlabel">
102+
<x>286</x>
103+
<y>274</y>
104+
</hint>
105+
</hints>
106+
</connection>
107+
</connections>
108+
</ui>

‎src/ui/qgsvectorlayerpropertiesbase.ui

Lines changed: 71 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<rect>
77
<x>0</x>
88
<y>0</y>
9-
<width>668</width>
9+
<width>591</width>
1010
<height>426</height>
1111
</rect>
1212
</property>
@@ -372,8 +372,8 @@
372372
<rect>
373373
<x>0</x>
374374
<y>0</y>
375-
<width>384</width>
376-
<height>408</height>
375+
<width>432</width>
376+
<height>419</height>
377377
</rect>
378378
</property>
379379
<layout class="QGridLayout" name="gridLayout_3">
@@ -611,6 +611,74 @@
611611
</item>
612612
</layout>
613613
</widget>
614+
<widget class="QWidget" name="mJoinPage">
615+
<attribute name="icon">
616+
<iconset resource="../../images/images.qrc">
617+
<normaloff>:/images/themes/default/propertyicons/join.png</normaloff>:/images/themes/default/propertyicons/join.png</iconset>
618+
</attribute>
619+
<attribute name="title">
620+
<string>Joins</string>
621+
</attribute>
622+
<layout class="QGridLayout" name="gridLayout_10">
623+
<item row="0" column="0">
624+
<widget class="QPushButton" name="mButtonAddJoin">
625+
<property name="text">
626+
<string/>
627+
</property>
628+
<property name="icon">
629+
<iconset resource="../../images/images.qrc">
630+
<normaloff>:/images/themes/default/symbologyAdd.png</normaloff>:/images/themes/default/symbologyAdd.png</iconset>
631+
</property>
632+
</widget>
633+
</item>
634+
<item row="0" column="1">
635+
<widget class="QPushButton" name="mButtonRemoveJoin">
636+
<property name="text">
637+
<string/>
638+
</property>
639+
<property name="icon">
640+
<iconset resource="../../images/images.qrc">
641+
<normaloff>:/images/themes/default/symbologyRemove.png</normaloff>:/images/themes/default/symbologyRemove.png</iconset>
642+
</property>
643+
</widget>
644+
</item>
645+
<item row="0" column="2">
646+
<spacer name="horizontalSpacer">
647+
<property name="orientation">
648+
<enum>Qt::Horizontal</enum>
649+
</property>
650+
<property name="sizeHint" stdset="0">
651+
<size>
652+
<width>354</width>
653+
<height>23</height>
654+
</size>
655+
</property>
656+
</spacer>
657+
</item>
658+
<item row="1" column="0" colspan="3">
659+
<widget class="QTreeWidget" name="mJoinTreeWidget">
660+
<property name="columnCount">
661+
<number>3</number>
662+
</property>
663+
<column>
664+
<property name="text">
665+
<string>Join layer</string>
666+
</property>
667+
</column>
668+
<column>
669+
<property name="text">
670+
<string>Join field</string>
671+
</property>
672+
</column>
673+
<column>
674+
<property name="text">
675+
<string>Target field</string>
676+
</property>
677+
</column>
678+
</widget>
679+
</item>
680+
</layout>
681+
</widget>
614682
</widget>
615683
</item>
616684
</layout>

0 commit comments

Comments
 (0)
Please sign in to comment.