Skip to content

Commit

Permalink
feature counts in rulebased widget
Browse files Browse the repository at this point in the history
  • Loading branch information
blazek committed Oct 31, 2012
1 parent fd7c3a2 commit c4014cf
Show file tree
Hide file tree
Showing 5 changed files with 204 additions and 8 deletions.
16 changes: 16 additions & 0 deletions src/core/symbology-ng/qgsrulebasedrendererv2.cpp
Expand Up @@ -415,6 +415,22 @@ QgsSymbolV2List QgsRuleBasedRendererV2::Rule::symbolsForFeature( QgsFeature& fea
return lst;
}

QgsRuleBasedRendererV2::RuleList QgsRuleBasedRendererV2::Rule::rulesForFeature( QgsFeature& feat )
{
RuleList lst;
if ( !isFilterOK( feat ) )
return lst;

if ( mSymbol )
lst.append( this );

for ( QList<Rule*>::iterator it = mActiveChildren.begin(); it != mActiveChildren.end(); ++it )
{
Rule* rule = *it;
lst += rule->rulesForFeature( feat );
}
return lst;
}

void QgsRuleBasedRendererV2::Rule::stopRender( QgsRenderContext& context )
{
Expand Down
4 changes: 4 additions & 0 deletions src/core/symbology-ng/qgsrulebasedrendererv2.h
Expand Up @@ -140,11 +140,15 @@ class CORE_EXPORT QgsRuleBasedRendererV2 : public QgsFeatureRendererV2
//! @note added in 1.9
QgsSymbolV2List symbolsForFeature( QgsFeature& feat );

//! tell which rules will be used to render the feature
RuleList rulesForFeature( QgsFeature& feat );

void stopRender( QgsRenderContext& context );

static Rule* create( QDomElement& ruleElem, QgsSymbolV2Map& symbolMap );

RuleList& children() { return mChildren; }
RuleList descendants() const { RuleList l; foreach ( Rule *c, mChildren ) { l += c; l += c->children(); } return l; }
Rule* parent() { return mParent; }

//! add child rule, take ownership, sets this as parent
Expand Down
160 changes: 153 additions & 7 deletions src/gui/symbology-ng/qgsrulebasedrendererv2widget.cpp
Expand Up @@ -26,6 +26,7 @@
#include "qstring.h"

#include <QMenu>
#include <QProgressDialog>
#include <QSettings>
#include <QTreeWidgetItem>
#include <QVBoxLayout>
Expand Down Expand Up @@ -86,6 +87,7 @@ QgsRuleBasedRendererV2Widget::QgsRuleBasedRendererV2Widget( QgsVectorLayer* laye
connect( btnAddRule, SIGNAL( clicked() ), this, SLOT( addRule() ) );
connect( btnEditRule, SIGNAL( clicked() ), this, SLOT( editRule() ) );
connect( btnRemoveRule, SIGNAL( clicked() ), this, SLOT( removeRule() ) );
connect( btnCountFeatures, SIGNAL( clicked() ), this, SLOT( countFeatures() ) );

connect( btnRenderingOrder, SIGNAL( clicked() ), this, SLOT( setRenderingOrder() ) );

Expand Down Expand Up @@ -129,6 +131,7 @@ void QgsRuleBasedRendererV2Widget::addRule()
int rows = mModel->rowCount();
mModel->insertRule( QModelIndex(), rows, newrule );
}
mModel->clearFeatureCounts();
}
else
{
Expand Down Expand Up @@ -161,6 +164,7 @@ void QgsRuleBasedRendererV2Widget::editRule( const QModelIndex& index )
{
// model should know about the change and emit dataChanged signal for the view
mModel->updateRule( index.parent(), index.row() );
mModel->clearFeatureCounts();
}
}

Expand All @@ -176,6 +180,7 @@ void QgsRuleBasedRendererV2Widget::removeRule()
}
// make sure that the selection is gone
viewRules->selectionModel()->clear();
mModel->clearFeatureCounts();
}

void QgsRuleBasedRendererV2Widget::currentRuleChanged( const QModelIndex& current, const QModelIndex& previous )
Expand Down Expand Up @@ -378,7 +383,7 @@ void QgsRuleBasedRendererV2Widget::saveSectionWidth( int section, int oldSize, i
{
Q_UNUSED( oldSize );
// skip last section, as it stretches
if ( section == 3 )
if ( section == 5 )
return;
QSettings settings;
QString path = "/Windows/RuleBasedTree/sectionWidth/" + QString::number( section );
Expand All @@ -390,11 +395,84 @@ void QgsRuleBasedRendererV2Widget::restoreSectionWidths()
QSettings settings;
QString path = "/Windows/RuleBasedTree/sectionWidth/";
QHeaderView* head = viewRules->header();
head->resizeSection( 0, settings.value( path + QString::number( 0 ), 200 ).toInt() );
head->resizeSection( 1, settings.value( path + QString::number( 1 ), 200 ).toInt() );
head->resizeSection( 2, settings.value( path + QString::number( 2 ), 100 ).toInt() );
head->resizeSection( 0, settings.value( path + QString::number( 0 ), 150 ).toInt() );
head->resizeSection( 1, settings.value( path + QString::number( 1 ), 150 ).toInt() );
head->resizeSection( 2, settings.value( path + QString::number( 2 ), 80 ).toInt() );
head->resizeSection( 3, settings.value( path + QString::number( 3 ), 80 ).toInt() );
head->resizeSection( 4, settings.value( path + QString::number( 4 ), 50 ).toInt() );
head->resizeSection( 5, settings.value( path + QString::number( 5 ), 50 ).toInt() );
}

void QgsRuleBasedRendererV2Widget::countFeatures()
{
if ( !mLayer || !mRenderer || !mRenderer->rootRule() )
{
return;
}
QMap<QgsRuleBasedRendererV2::Rule*, QgsRuleBasedRendererV2Count> countMap;

QgsRuleBasedRendererV2::RuleList ruleList = mRenderer->rootRule()->descendants();
// insert all so that we have counts 0
foreach ( QgsRuleBasedRendererV2::Rule* rule, ruleList )
{
countMap[rule].count = 0;
countMap[rule].duplicateCount = 0;
}

mLayer->select( mLayer->pendingAllAttributesList(), QgsRectangle(), false, false );

QgsRenderContext renderContext;
renderContext.setRendererScale( 0 ); // ignore scale
mRenderer->startRender( renderContext, mLayer );

int nFeatures = mLayer->pendingFeatureCount();
QProgressDialog p( tr( "Calculating feature count." ), tr( "Abort" ), 0, nFeatures );
p.setWindowModality( Qt::WindowModal );
int featuresCounted = 0;

QgsFeature f;
while ( mLayer->nextFeature( f ) )
{
QgsRuleBasedRendererV2::RuleList featureRuleList = mRenderer->rootRule()->rulesForFeature( f );

foreach ( QgsRuleBasedRendererV2::Rule* rule, featureRuleList )
{
countMap[rule].count++;
if ( featureRuleList.size() > 1 )
{
countMap[rule].duplicateCount++;
}
foreach ( QgsRuleBasedRendererV2::Rule* duplicateRule, featureRuleList )
{
if ( duplicateRule == rule ) continue;
countMap[rule].duplicateCountMap[duplicateRule] += 1;
}
}
++featuresCounted;
if ( featuresCounted % 50 == 0 )
{
if ( featuresCounted > nFeatures ) //sometimes the feature count is not correct
{
p.setMaximum( 0 );
}
p.setValue( featuresCounted );
if ( p.wasCanceled() )
{
return;
}
}
}
p.setValue( nFeatures );

mRenderer->stopRender( renderContext );

foreach ( QgsRuleBasedRendererV2::Rule *rule, countMap.keys() )
{
QgsDebugMsg( QString( "rule: %1 count %2" ).arg( rule->label() ).arg( countMap[rule].count ) );
}

mModel->setFeatureCounts( countMap );
}

///////////

Expand Down Expand Up @@ -556,6 +634,39 @@ QVariant QgsRuleBasedRendererV2Model::data( const QModelIndex &index, int role )
case 1: return rule->filterExpression().isEmpty() ? tr( "(no filter)" ) : rule->filterExpression();
case 2: return rule->dependsOnScale() ? _formatScale( rule->scaleMinDenom() ) : QVariant();
case 3: return rule->dependsOnScale() ? _formatScale( rule->scaleMaxDenom() ) : QVariant();
case 4:
if ( mFeatureCountMap.count( rule ) == 1 )
{
return QVariant( mFeatureCountMap[rule].count );
}
return QVariant();
case 5:
if ( mFeatureCountMap.count( rule ) == 1 )
{
if ( role == Qt::DisplayRole )
{
return QVariant( mFeatureCountMap[rule].duplicateCount );
}
else // tooltip - detailed info about duplicates
{
if ( mFeatureCountMap[rule].duplicateCount > 0 )
{
QString tip = "<p style='margin:0px;'><ul>";
foreach ( QgsRuleBasedRendererV2::Rule* duplicateRule, mFeatureCountMap[rule].duplicateCountMap.keys() )
{
QString label = duplicateRule->label().replace( "&", "&amp;" ).replace( ">", "&gt;" ).replace( "<", "&lt;" );
tip += tr( "<li><nobr>%1 features also in rule %2</nobr></li>" ).arg( mFeatureCountMap[rule].duplicateCountMap[duplicateRule] ).arg( label );
}
tip += "</ul>";
return tip;
}
else
{
return 0;
}
}
}
return QVariant();
default: return QVariant();
}
}
Expand Down Expand Up @@ -584,11 +695,22 @@ QVariant QgsRuleBasedRendererV2Model::data( const QModelIndex &index, int role )

QVariant QgsRuleBasedRendererV2Model::headerData( int section, Qt::Orientation orientation, int role ) const
{
if ( orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= 0 && section < 5 )
if ( orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= 0 && section < 7 )
{
QStringList lst; lst << tr( "Label" ) << tr( "Rule" ) << tr( "Min. scale" ) << tr( "Max.scale" );
QStringList lst; lst << tr( "Label" ) << tr( "Rule" ) << tr( "Min. scale" ) << tr( "Max.scale" ) << tr( "Count" ) << tr( "Duplicate count" );
return lst[section];
}
else if ( orientation == Qt::Horizontal && role == Qt::ToolTipRole )
{
if ( section == 4 ) // Count
{
return tr( "Number of features in this rule." );
}
else if ( section == 5 ) // Duplicate count
{
return tr( "Number of features in this rule which are also present in other rule(s)." );
}
}

return QVariant();
}
Expand All @@ -605,7 +727,7 @@ int QgsRuleBasedRendererV2Model::rowCount( const QModelIndex &parent ) const

int QgsRuleBasedRendererV2Model::columnCount( const QModelIndex & ) const
{
return 4;
return 6;
}

QModelIndex QgsRuleBasedRendererV2Model::index( int row, int column, const QModelIndex &parent ) const
Expand Down Expand Up @@ -813,6 +935,18 @@ void QgsRuleBasedRendererV2Model::updateRule( const QModelIndex& parent, int row
index( row, columnCount( parent ), parent ) );
}

void QgsRuleBasedRendererV2Model::updateRule( const QModelIndex& idx )
{
emit dataChanged( index( 0, 0, idx ),
index( rowCount( idx ) - 1, columnCount( idx ) - 1, idx ) );

for ( int i; i < rowCount( idx ); i++ )
{
updateRule( index( i, 0, idx ) );
}
}


void QgsRuleBasedRendererV2Model::removeRule( const QModelIndex& index )
{
if ( !index.isValid() )
Expand All @@ -836,3 +970,15 @@ void QgsRuleBasedRendererV2Model::finishedAddingRules()
{
emit endInsertRows();
}

void QgsRuleBasedRendererV2Model::setFeatureCounts( QMap<QgsRuleBasedRendererV2::Rule*, QgsRuleBasedRendererV2Count> theCountMap )
{
mFeatureCountMap = theCountMap;
updateRule( QModelIndex() );
}

void QgsRuleBasedRendererV2Model::clearFeatureCounts()
{
mFeatureCountMap.clear();
updateRule( QModelIndex() );
}
17 changes: 17 additions & 0 deletions src/gui/symbology-ng/qgsrulebasedrendererv2widget.h
Expand Up @@ -25,6 +25,15 @@ class QMenu;

#include <QAbstractItemModel>

/* Features count fro rule */
struct QgsRuleBasedRendererV2Count
{
int count; // number of features
int duplicateCount; // number of features present also in other rule(s)
// map of feature counts in other rules
QMap<QgsRuleBasedRendererV2::Rule*, int> duplicateCountMap;
};

/*
Tree model for the rules:
Expand Down Expand Up @@ -65,13 +74,19 @@ class GUI_EXPORT QgsRuleBasedRendererV2Model : public QAbstractItemModel

void insertRule( const QModelIndex& parent, int before, QgsRuleBasedRendererV2::Rule* newrule );
void updateRule( const QModelIndex& parent, int row );
// update rule and all its descendants
void updateRule( const QModelIndex& index );
void removeRule( const QModelIndex& index );

void willAddRules( const QModelIndex& parent, int count ); // call beginInsertRows
void finishedAddingRules(); // call endInsertRows

void setFeatureCounts( QMap<QgsRuleBasedRendererV2::Rule*, QgsRuleBasedRendererV2Count> theCountMap );
void clearFeatureCounts();

protected:
QgsRuleBasedRendererV2* mR;
QMap<QgsRuleBasedRendererV2::Rule*, QgsRuleBasedRendererV2Count> mFeatureCountMap;
};


Expand All @@ -98,6 +113,8 @@ class GUI_EXPORT QgsRuleBasedRendererV2Widget : public QgsRendererV2Widget, priv
void editRule();
void editRule( const QModelIndex& index );
void removeRule();
void countFeatures();
void clearFeatureCounts() { mModel->clearFeatureCounts(); }

void refineRuleScales();
void refineRuleCategories();
Expand Down
15 changes: 14 additions & 1 deletion src/ui/qgsrulebasedrendererv2widget.ui
Expand Up @@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>640</width>
<width>643</width>
<height>401</height>
</rect>
</property>
Expand Down Expand Up @@ -43,6 +43,12 @@
<attribute name="headerStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="headerMinimumSectionSize">
<number>100</number>
</attribute>
<attribute name="headerStretchLastSection">
<bool>true</bool>
</attribute>
</widget>
</item>
<item>
Expand Down Expand Up @@ -90,6 +96,13 @@
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnCountFeatures">
<property name="text">
<string>Count features</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
Expand Down

0 comments on commit c4014cf

Please sign in to comment.