Index: src/gui/symbology-ng/qgsrulebasedrendererv2widget.h =================================================================== --- src/gui/symbology-ng/qgsrulebasedrendererv2widget.h (revision 0) +++ src/gui/symbology-ng/qgsrulebasedrendererv2widget.h (revision 0) @@ -0,0 +1,117 @@ +/*************************************************************************** + qgsrulebasedrendererv2widget.h - Settings widget for rule-based renderer + --------------------- + begin : May 2010 + copyright : (C) 2010 by Martin Dobias + email : wonder.sk at gmail.com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef QGSRULEBASEDRENDERERV2WIDGET_H +#define QGSRULEBASEDRENDERERV2WIDGET_H + +#include "qgsrendererv2widget.h" + +#include "qgsrulebasedrendererv2.h" +class QMenu; + +/////// + +#include + +class QgsRendererRulesTreeWidget : public QTreeWidget +{ + public: + QgsRendererRulesTreeWidget( QWidget* parent = 0 ); + + void setRenderer( QgsRuleBasedRendererV2* r ); + + enum Grouping { NoGrouping, GroupingByScale, GroupingByFilter }; + + void setGrouping( Grouping g ); + + void populateRules(); + + protected: + void populateRulesNoGrouping(); + void populateRulesGroupByScale(); + void populateRulesGroupByFilter(); + + QgsRuleBasedRendererV2* mR; + Grouping mGrouping; +}; + +/////// + +#include "ui_qgsrulebasedrendererv2widget.h" + +class GUI_EXPORT QgsRuleBasedRendererV2Widget : public QgsRendererV2Widget, private Ui::QgsRuleBasedRendererV2Widget +{ + Q_OBJECT + + public: + + static QgsRendererV2Widget* create( QgsVectorLayer* layer, QgsStyleV2* style, QgsFeatureRendererV2* renderer ); + + QgsRuleBasedRendererV2Widget( QgsVectorLayer* layer, QgsStyleV2* style, QgsFeatureRendererV2* renderer ); + ~QgsRuleBasedRendererV2Widget(); + + virtual QgsFeatureRendererV2* renderer(); + + public slots: + + void addRule(); + void editRule(); + void removeRule(); + + void setGrouping(); + + void refineRuleScales(); + void refineRuleCategories(); + void refineRuleRanges(); + + protected: + + void refineRule( int type ); + QList refineRuleCategoriesGui( QgsRuleBasedRendererV2::Rule& initialRule ); + QList refineRuleRangesGui( QgsRuleBasedRendererV2::Rule& initialRule ); + QList refineRuleScalesGui( QgsRuleBasedRendererV2::Rule& initialRule ); + + QgsRuleBasedRendererV2* mRenderer; + + QMenu* mRefineMenu; +}; + +/////// + +#include + +#include "ui_qgsrendererrulepropsdialogbase.h" + +class GUI_EXPORT QgsRendererRulePropsDialog : public QDialog, private Ui::QgsRendererRulePropsDialog +{ + Q_OBJECT + + public: + QgsRendererRulePropsDialog( const QgsRuleBasedRendererV2::Rule& rule, QgsVectorLayer* layer, QgsStyleV2* style ); + + void updateRuleFromGui(); + const QgsRuleBasedRendererV2::Rule& rule() { return mRule; } + + public slots: + void testFilter(); + + protected: + QgsRuleBasedRendererV2::Rule mRule; + QgsVectorLayer* mLayer; + QgsStyleV2* mStyle; +}; + + +#endif // QGSRULEBASEDRENDERERV2WIDGET_H Index: src/gui/symbology-ng/qgsrulebasedrendererv2widget.cpp =================================================================== --- src/gui/symbology-ng/qgsrulebasedrendererv2widget.cpp (revision 0) +++ src/gui/symbology-ng/qgsrulebasedrendererv2widget.cpp (revision 0) @@ -0,0 +1,493 @@ +/*************************************************************************** + qgsrulebasedrendererv2widget.cpp - Settings widget for rule-based renderer + --------------------- + begin : May 2010 + copyright : (C) 2010 by Martin Dobias + email : wonder.sk at gmail.com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgsrulebasedrendererv2widget.h" + +#include "qgsrulebasedrendererv2.h" +#include "qgssymbollayerv2utils.h" +#include "qgssymbolv2.h" +#include "qgsvectorlayer.h" +#include "qgsapplication.h" +#include "qgssearchtreenode.h" +#include "qgssymbolv2selectordialog.h" + +#include +#include +#include +#include + +QgsRendererV2Widget* QgsRuleBasedRendererV2Widget::create( QgsVectorLayer* layer, QgsStyleV2* style, QgsFeatureRendererV2* renderer ) +{ + return new QgsRuleBasedRendererV2Widget( layer, style, renderer ); +} + +QgsRuleBasedRendererV2Widget::QgsRuleBasedRendererV2Widget( QgsVectorLayer* layer, QgsStyleV2* style, QgsFeatureRendererV2* renderer ) + : QgsRendererV2Widget( layer, style ) +{ + + // try to recognize the previous renderer + // (null renderer means "no previous renderer") + if ( !renderer || renderer->type() != "RuleRenderer" ) + { + // we're not going to use it - so let's delete the renderer + delete renderer; + + // some default options + QgsSymbolV2* symbol = QgsSymbolV2::defaultSymbol( mLayer->geometryType() ); + + mRenderer = new QgsRuleBasedRendererV2( symbol ); + } + else + { + mRenderer = static_cast( renderer ); + } + + setupUi( this ); + + treeRules->setRenderer( mRenderer ); + + mRefineMenu = new QMenu( btnRefineRule ); + mRefineMenu->addAction( tr( "Add scales" ), this, SLOT( refineRuleScales() ) ); + mRefineMenu->addAction( tr( "Add categories" ), this, SLOT( refineRuleCategories() ) ); + mRefineMenu->addAction( tr( "Add ranges" ), this, SLOT( refineRuleRanges() ) ); + btnRefineRule->setMenu( mRefineMenu ); + + btnAddRule->setIcon( QIcon( QgsApplication::iconPath( "symbologyAdd.png" ) ) ); + btnEditRule->setIcon( QIcon( QgsApplication::iconPath( "symbologyEdit.png" ) ) ); + btnRemoveRule->setIcon( QIcon( QgsApplication::iconPath( "symbologyRemove.png" ) ) ); + + connect( treeRules, SIGNAL( itemDoubleClicked( QTreeWidgetItem*, int ) ), this, SLOT( editRule() ) ); + + connect( btnAddRule, SIGNAL( clicked() ), this, SLOT( addRule() ) ); + connect( btnEditRule, SIGNAL( clicked() ), this, SLOT( editRule() ) ); + connect( btnRemoveRule, SIGNAL( clicked() ), this, SLOT( removeRule() ) ); + + connect( radNoGrouping, SIGNAL( clicked() ), this, SLOT( setGrouping() ) ); + connect( radGroupFilter, SIGNAL( clicked() ), this, SLOT( setGrouping() ) ); + connect( radGroupScale, SIGNAL( clicked() ), this, SLOT( setGrouping() ) ); + + treeRules->populateRules(); +} + +QgsRuleBasedRendererV2Widget::~QgsRuleBasedRendererV2Widget() +{ + delete mRenderer; +} + +QgsFeatureRendererV2* QgsRuleBasedRendererV2Widget::renderer() +{ + return mRenderer; +} + +void QgsRuleBasedRendererV2Widget::setGrouping() +{ + QObject* origin = sender(); + if ( origin == radNoGrouping ) + treeRules->setGrouping( QgsRendererRulesTreeWidget::NoGrouping ); + else if ( origin == radGroupFilter ) + treeRules->setGrouping( QgsRendererRulesTreeWidget::GroupingByFilter ); + else if ( origin == radGroupScale ) + treeRules->setGrouping( QgsRendererRulesTreeWidget::GroupingByScale ); +} + +void QgsRuleBasedRendererV2Widget::addRule() +{ + QgsRuleBasedRendererV2::Rule newrule( QgsSymbolV2::defaultSymbol( mLayer->geometryType() ) ); + + QgsRendererRulePropsDialog dlg( newrule, mLayer, mStyle ); + if ( dlg.exec() ) + { + // add rule + dlg.updateRuleFromGui(); + mRenderer->addRule( dlg.rule() ); + treeRules->populateRules(); + } +} + + + +void QgsRuleBasedRendererV2Widget::editRule() +{ + QTreeWidgetItem * item = treeRules->currentItem(); + if ( ! item ) return; + + int rule_index = item->data( 0, Qt::UserRole + 1 ).toInt(); + if ( rule_index < 0 ) + { + QMessageBox::information( this, tr( "Edit rule" ), tr( "Groups of rules cannot be edited." ) ); + return; + } + QgsRuleBasedRendererV2::Rule& rule = mRenderer->ruleAt( rule_index ); + + QgsRendererRulePropsDialog dlg( rule, mLayer, mStyle ); + if ( dlg.exec() ) + { + // update rule + dlg.updateRuleFromGui(); + mRenderer->updateRuleAt( rule_index, dlg.rule() ); + + treeRules->populateRules(); + } +} + +void QgsRuleBasedRendererV2Widget::removeRule() +{ + QTreeWidgetItem * item = treeRules->currentItem(); + if ( ! item ) return; + + int rule_index = item->data( 0, Qt::UserRole + 1 ).toInt(); + if ( rule_index < 0 ) + { + QList rule_indexes; + // this is a group + for ( int i = 0; i < item->childCount(); i++ ) + { + int idx = item->child( i )->data( 0, Qt::UserRole + 1 ).toInt(); + rule_indexes.append( idx ); + } + // delete in decreasing order to avoid shifting of indexes + qSort( rule_indexes.begin(), rule_indexes.end(), qGreater() ); + foreach( int idx, rule_indexes ) + mRenderer->removeRuleAt( idx ); + } + else + { + // this is a rule + mRenderer->removeRuleAt( rule_index ); + } + + treeRules->populateRules(); +} + + +#include "qgscategorizedsymbolrendererv2.h" +#include "qgscategorizedsymbolrendererv2widget.h" +#include "qgsgraduatedsymbolrendererv2.h" +#include "qgsgraduatedsymbolrendererv2widget.h" +#include +#include + +void QgsRuleBasedRendererV2Widget::refineRule( int type ) +{ + QTreeWidgetItem * item = treeRules->currentItem(); + if ( ! item ) return; + + int rule_index = item->data( 0, Qt::UserRole + 1 ).toInt(); + if ( rule_index < 0 ) return; + + QgsRuleBasedRendererV2::Rule& initialRule = mRenderer->ruleAt( rule_index ); + + + QList refinedRules; + if ( type == 0 ) // categories + refinedRules = refineRuleCategoriesGui( initialRule ); + else if ( type == 1 ) // ranges + refinedRules = refineRuleRangesGui( initialRule ); + else // scales + refinedRules = refineRuleScalesGui( initialRule ); + + if ( refinedRules.count() == 0 ) + return; + + // delete original rule + mRenderer->removeRuleAt( rule_index ); + + // add new rules + for ( int i = 0; i < refinedRules.count(); i++ ) + { + mRenderer->insertRule( rule_index + i, refinedRules.at( i ) ); + } + + treeRules->populateRules(); +} + +void QgsRuleBasedRendererV2Widget::refineRuleCategories() +{ + refineRule( 0 ); +} + +void QgsRuleBasedRendererV2Widget::refineRuleRanges() +{ + refineRule( 1 ); +} + +void QgsRuleBasedRendererV2Widget::refineRuleScales() +{ + refineRule( 2 ); +} + +QList QgsRuleBasedRendererV2Widget::refineRuleCategoriesGui( QgsRuleBasedRendererV2::Rule& initialRule ) +{ + QDialog dlg; + dlg.setWindowTitle( tr( "Refine a rule to categories" ) ); + QVBoxLayout* l = new QVBoxLayout(); + QgsCategorizedSymbolRendererV2Widget* w = new QgsCategorizedSymbolRendererV2Widget( mLayer, mStyle, NULL ); + l->addWidget( w ); + QDialogButtonBox* bb = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel ); + l->addWidget( bb ); + connect( bb, SIGNAL( accepted() ), &dlg, SLOT( accept() ) ); + connect( bb, SIGNAL( rejected() ), &dlg, SLOT( reject() ) ); + dlg.setLayout( l ); + + if ( !dlg.exec() ) + return QList(); + + // create new rules + QgsCategorizedSymbolRendererV2* r = static_cast( w->renderer() ); + return QgsRuleBasedRendererV2::refineRuleCategories( initialRule, r ); +} + + +QList QgsRuleBasedRendererV2Widget::refineRuleRangesGui( QgsRuleBasedRendererV2::Rule& initialRule ) +{ + QDialog dlg; + dlg.setWindowTitle( tr( "Refine a rule to ranges" ) ); + QVBoxLayout* l = new QVBoxLayout(); + QgsGraduatedSymbolRendererV2Widget* w = new QgsGraduatedSymbolRendererV2Widget( mLayer, mStyle, NULL ); + l->addWidget( w ); + QDialogButtonBox* bb = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel ); + l->addWidget( bb ); + connect( bb, SIGNAL( accepted() ), &dlg, SLOT( accept() ) ); + connect( bb, SIGNAL( rejected() ), &dlg, SLOT( reject() ) ); + dlg.setLayout( l ); + + if ( !dlg.exec() ) + return QList(); + + // create new rules + QgsGraduatedSymbolRendererV2* r = static_cast( w->renderer() ); + return QgsRuleBasedRendererV2::refineRuleRanges( initialRule, r ); +} + +QList QgsRuleBasedRendererV2Widget::refineRuleScalesGui( QgsRuleBasedRendererV2::Rule& initialRule ) +{ + QString txt = QInputDialog::getText( this, + tr( "Scale refinement" ), + tr( "Please enter scale denominators at which will split the rule, separate them by commas (e.g. 1000,5000):" ) ); + if ( txt.isEmpty() ) + return QList(); + + QList scales; + bool ok; + foreach( QString item, txt.split( ',' ) ) + { + int scale = item.toInt( &ok ); + if ( ok ) + scales.append( scale ); + else + QMessageBox::information( this, tr( "Error" ), QString( tr( "\"%1\" is not valid scale denominator, ignoring it." ) ).arg( item ) ); + } + + return QgsRuleBasedRendererV2::refineRuleScales( initialRule, scales ); +} + + +/////////// + +QgsRendererRulePropsDialog::QgsRendererRulePropsDialog( const QgsRuleBasedRendererV2::Rule& rule, QgsVectorLayer* layer, QgsStyleV2* style ) + : mRule( rule ), mLayer( layer ) +{ + setupUi( this ); + + editFilter->setText( mRule.filterExpression() ); + + if ( mRule.dependsOnScale() ) + { + groupScale->setChecked( true ); + spinMinScale->setValue( rule.scaleMinDenom() ); + spinMaxScale->setValue( rule.scaleMaxDenom() ); + } + + QgsSymbolV2SelectorDialog* symbolSel = new QgsSymbolV2SelectorDialog( mRule.symbol(), style, this, true ); + QVBoxLayout* l = new QVBoxLayout; + l->addWidget( symbolSel ); + groupSymbol->setLayout( l ); + + connect( btnTestFilter, SIGNAL( clicked() ), this, SLOT( testFilter() ) ); +} + +void QgsRendererRulePropsDialog::testFilter() +{ + QgsSearchString filterParsed; + if ( ! filterParsed.setString( editFilter->text() ) ) + { + QMessageBox::critical( this, tr( "Error" ), tr( "Filter expression parsing error:\n" ) + filterParsed.parserErrorMsg() ); + return; + } + + QgsSearchTreeNode* tree = filterParsed.tree(); + if ( ! tree ) + { + QMessageBox::critical( this, tr( "Error" ), tr( "Filter is empty" ) ); + return; + } + + QApplication::setOverrideCursor( Qt::WaitCursor ); + + const QgsFieldMap& fields = mLayer->pendingFields(); + mLayer->select( fields.keys(), QgsRectangle(), false ); + + int count = 0; + QgsFeature f; + while ( mLayer->nextFeature( f ) ) + { + if ( tree->checkAgainst( fields, f.attributeMap() ) ) + count++; + if ( tree->hasError() ) + break; + } + + QApplication::restoreOverrideCursor(); + + QMessageBox::information( this, tr( "Filter" ), tr( "Filter returned %1 features" ).arg( count ) ); +} + +void QgsRendererRulePropsDialog::updateRuleFromGui() +{ + mRule.setFilterExpression( editFilter->text() ); + mRule.setScaleMinDenom( groupScale->isChecked() ? spinMinScale->value() : 0 ); + mRule.setScaleMaxDenom( groupScale->isChecked() ? spinMaxScale->value() : 0 ); +} + +//////// + +QgsRendererRulesTreeWidget::QgsRendererRulesTreeWidget( QWidget* parent ) + : QTreeWidget( parent ), mR( NULL ), mGrouping( NoGrouping ) +{ + setSelectionMode( QAbstractItemView::SingleSelection ); + /* + setDragEnabled(true); + viewport()->setAcceptDrops(true); + setDropIndicatorShown(true); + setDragDropMode(QAbstractItemView::InternalMove); + */ +} + +void QgsRendererRulesTreeWidget::setRenderer( QgsRuleBasedRendererV2* r ) +{ + mR = r; +} + +void QgsRendererRulesTreeWidget::setGrouping( Grouping g ) +{ + mGrouping = g; + setRootIsDecorated( mGrouping != NoGrouping ); + populateRules(); +} + +void QgsRendererRulesTreeWidget::populateRules() +{ + if ( !mR ) return; + + clear(); + if ( mGrouping == NoGrouping ) + populateRulesNoGrouping(); + else if ( mGrouping == GroupingByScale ) + populateRulesGroupByScale(); + else + populateRulesGroupByFilter(); +} + +void QgsRendererRulesTreeWidget::populateRulesNoGrouping() +{ + QList lst; + for ( int i = 0; i < mR->ruleCount(); ++i ) + { + QgsRuleBasedRendererV2::Rule& rule = mR->ruleAt( i ); + + QTreeWidgetItem* item = new QTreeWidgetItem; + QString txt = rule.filterExpression(); + if ( txt.isEmpty() ) txt = tr( "(no filter)" ); + if ( rule.dependsOnScale() ) + { + txt += QString( ", scale <1:%1, 1:%2>" ).arg( rule.scaleMinDenom() ).arg( rule.scaleMaxDenom() ); + } + + item->setText( 0, txt ); + item->setData( 0, Qt::UserRole + 1, i ); + item->setIcon( 0, QgsSymbolLayerV2Utils::symbolPreviewIcon( rule.symbol(), QSize( 16, 16 ) ) ); + + lst << item; + } + + addTopLevelItems( lst ); +} + +void QgsRendererRulesTreeWidget::populateRulesGroupByScale() +{ + QMap< QPair, QTreeWidgetItem*> scale_items; + + for ( int i = 0; i < mR->ruleCount(); ++i ) + { + QgsRuleBasedRendererV2::Rule& rule = mR->ruleAt( i ); + + QPair scale = qMakePair( rule.scaleMinDenom(), rule.scaleMaxDenom() ); + if ( ! scale_items.contains( scale ) ) + { + QString txt; + if ( rule.dependsOnScale() ) + txt = QString( "scale <1:%1, 1:%2>" ).arg( rule.scaleMinDenom() ).arg( rule.scaleMaxDenom() ); + else + txt = "any scale"; + + QTreeWidgetItem* scale_item = new QTreeWidgetItem; + scale_item->setText( 0, txt ); + scale_item->setData( 0, Qt::UserRole + 1, -2 ); + scale_item->setFlags( scale_item->flags() & ~Qt::ItemIsDragEnabled ); // groups cannot be dragged + scale_items[scale] = scale_item; + } + + QString filter = rule.filterExpression(); + + QTreeWidgetItem* item = new QTreeWidgetItem( scale_items[scale] ); + item->setText( 0, filter.isEmpty() ? tr( "(no filter)" ) : filter ); + item->setData( 0, Qt::UserRole + 1, i ); + item->setIcon( 0, QgsSymbolLayerV2Utils::symbolPreviewIcon( rule.symbol(), QSize( 16, 16 ) ) ); + } + addTopLevelItems( scale_items.values() ); +} + +void QgsRendererRulesTreeWidget::populateRulesGroupByFilter() +{ + QMap filter_items; + + for ( int i = 0; i < mR->ruleCount(); ++i ) + { + QgsRuleBasedRendererV2::Rule& rule = mR->ruleAt( i ); + + QString filter = rule.filterExpression(); + if ( ! filter_items.contains( filter ) ) + { + QTreeWidgetItem* filter_item = new QTreeWidgetItem; + filter_item->setText( 0, filter.isEmpty() ? tr( "(no filter)" ) : filter ); + filter_item->setData( 0, Qt::UserRole + 1, -1 ); + filter_item->setFlags( filter_item->flags() & ~Qt::ItemIsDragEnabled ); // groups cannot be dragged + filter_items[filter] = filter_item; + } + + QString txt; + if ( rule.dependsOnScale() ) + txt = QString( "scale <1:%1, 1:%2>" ).arg( rule.scaleMinDenom() ).arg( rule.scaleMaxDenom() ); + else + txt = "any scale"; + + QTreeWidgetItem* item = new QTreeWidgetItem( filter_items[filter] ); + item->setText( 0, txt ); + item->setData( 0, Qt::UserRole + 1, i ); + item->setIcon( 0, QgsSymbolLayerV2Utils::symbolPreviewIcon( rule.symbol(), QSize( 16, 16 ) ) ); + + } + addTopLevelItems( filter_items.values() ); +} Index: src/gui/symbology-ng/qgsrendererv2propertiesdialog.cpp =================================================================== --- src/gui/symbology-ng/qgsrendererv2propertiesdialog.cpp (revision 13699) +++ src/gui/symbology-ng/qgsrendererv2propertiesdialog.cpp (working copy) @@ -8,6 +8,7 @@ #include "qgssinglesymbolrendererv2widget.h" #include "qgscategorizedsymbolrendererv2widget.h" #include "qgsgraduatedsymbolrendererv2widget.h" +#include "qgsrulebasedrendererv2widget.h" #include "qgssymbollevelsv2dialog.h" @@ -18,7 +19,7 @@ #include #include -static bool _initRenderer( QString name, QgsRendererV2WidgetFunc f, QString iconName ) +static bool _initRenderer( QString name, QgsRendererV2WidgetFunc f, QString iconName = QString() ) { QgsRendererV2Registry* reg = QgsRendererV2Registry::instance(); QgsRendererV2AbstractMetadata* am = reg->rendererMetadata( name ); @@ -30,10 +31,13 @@ m->setWidgetFunction( f ); - QString iconPath = QgsApplication::defaultThemePath() + iconName; - QPixmap pix; - if ( pix.load( iconPath, "png" ) ) - m->setIcon( pix ); + if ( !iconName.isEmpty() ) + { + QString iconPath = QgsApplication::defaultThemePath() + iconName; + QPixmap pix; + if ( pix.load( iconPath, "png" ) ) + m->setIcon( pix ); + } QgsDebugMsg( "Set for " + name ); return true; @@ -48,6 +52,7 @@ _initRenderer( "singleSymbol", QgsSingleSymbolRendererV2Widget::create, "rendererSingleSymbol.png" ); _initRenderer( "categorizedSymbol", QgsCategorizedSymbolRendererV2Widget::create, "rendererCategorizedSymbol.png" ); _initRenderer( "graduatedSymbol", QgsGraduatedSymbolRendererV2Widget::create, "rendererGraduatedSymbol.png" ); + _initRenderer( "RuleRenderer", QgsRuleBasedRendererV2Widget::create ); initialized = true; } Index: src/gui/CMakeLists.txt =================================================================== --- src/gui/CMakeLists.txt (revision 13699) +++ src/gui/CMakeLists.txt (working copy) @@ -10,6 +10,7 @@ symbology-ng/qgssinglesymbolrendererv2widget.cpp symbology-ng/qgscategorizedsymbolrendererv2widget.cpp symbology-ng/qgsgraduatedsymbolrendererv2widget.cpp +symbology-ng/qgsrulebasedrendererv2widget.cpp symbology-ng/qgsrendererv2propertiesdialog.cpp symbology-ng/qgsstylev2managerdialog.cpp symbology-ng/qgssymbollevelsv2dialog.cpp @@ -63,6 +64,7 @@ symbology-ng/qgssinglesymbolrendererv2widget.h symbology-ng/qgscategorizedsymbolrendererv2widget.h symbology-ng/qgsgraduatedsymbolrendererv2widget.h +symbology-ng/qgsrulebasedrendererv2widget.h symbology-ng/qgsrendererv2propertiesdialog.h symbology-ng/qgsstylev2managerdialog.h symbology-ng/qgssymbollevelsv2dialog.h Index: src/core/symbology-ng/qgsrendererv2registry.cpp =================================================================== --- src/core/symbology-ng/qgsrendererv2registry.cpp (revision 13699) +++ src/core/symbology-ng/qgsrendererv2registry.cpp (working copy) @@ -4,8 +4,8 @@ #include "qgssinglesymbolrendererv2.h" #include "qgscategorizedsymbolrendererv2.h" #include "qgsgraduatedsymbolrendererv2.h" +#include "qgsrulebasedrendererv2.h" - QgsRendererV2Registry* QgsRendererV2Registry::mInstance = NULL; QgsRendererV2Registry::QgsRendererV2Registry() @@ -20,6 +20,10 @@ addRenderer( new QgsRendererV2Metadata( "graduatedSymbol", QObject::tr( "Graduated" ), QgsGraduatedSymbolRendererV2::create ) ); + + addRenderer( new QgsRendererV2Metadata( "RuleRenderer", + QObject::tr( "Rule-based" ), + QgsRuleBasedRendererV2::create ) ); } QgsRendererV2Registry::~QgsRendererV2Registry() Index: src/core/symbology-ng/qgsrulebasedrendererv2.h =================================================================== --- src/core/symbology-ng/qgsrulebasedrendererv2.h (revision 0) +++ src/core/symbology-ng/qgsrulebasedrendererv2.h (revision 0) @@ -0,0 +1,141 @@ +/*************************************************************************** + qgsrulebasedrendererv2.h - Rule-based renderer (symbology-ng) + --------------------- + begin : May 2010 + copyright : (C) 2010 by Martin Dobias + email : wonder.sk at gmail.com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef QGSRULEBASEDRENDERERV2_H +#define QGSRULEBASEDRENDERERV2_H + +#include "qgsfield.h" +#include "qgssearchstring.h" + +#include "qgsrendererv2.h" + +class QgsCategorizedSymbolRendererV2; +class QgsGraduatedSymbolRendererV2; + +/** +When drawing a vector layer with rule-based renderer, it goes through +the rules and draws features with symbols from rules that match. + */ +class CORE_EXPORT QgsRuleBasedRendererV2 : public QgsFeatureRendererV2 +{ + public: + + /** + This class keeps data about a rules for rule-based renderer. + A rule consists of a symbol, filter expression and range of scales. + If filter is empty, it matches all features. + If scale range has both values zero, it matches all scales. + If one of the min/max scale denominators is zero, there is no lower/upper bound for scales. + A rule matches if both filter and scale range match. + */ + class Rule + { + public: + //! Constructor takes ownership of the symbol + Rule( QgsSymbolV2* symbol, int scaleMinDenom = 0, int scaleMaxDenom = 0, QString filterExp = QString() ); + Rule( const Rule& other ); + ~Rule(); + QString dump() const; + QStringList needsFields() const; + bool isFilterOK( const QgsFieldMap& fields, QgsFeature& f ) const; + bool isScaleOK( double scale ) const; + + QgsSymbolV2* symbol() { return mSymbol; } + bool dependsOnScale() const { return mScaleMinDenom != 0 || mScaleMaxDenom != 0; } + int scaleMinDenom() const { return mScaleMinDenom; } + int scaleMaxDenom() const { return mScaleMaxDenom; } + QString filterExpression() const { return mFilterExp; } + + void setScaleMinDenom( int scaleMinDenom ) { mScaleMinDenom = scaleMinDenom; } + void setScaleMaxDenom( int scaleMaxDenom ) { mScaleMaxDenom = scaleMaxDenom; } + void setFilterExpression( QString filterExp ) { mFilterExp = filterExp; initFilter(); } + + Rule& operator=( const Rule& other ); + + protected: + + void initFilter(); + + QgsSymbolV2* mSymbol; + int mScaleMinDenom, mScaleMaxDenom; + QString mFilterExp; + + // temporary + QgsSearchString mFilterParsed; + QgsSearchTreeNode* mFilterTree; + }; + + ///// + + static QgsFeatureRendererV2* create( QDomElement& element ); + + //! Constructor. Takes ownership of the defult symbol. + QgsRuleBasedRendererV2( QgsSymbolV2* defaultSymbol ); + + //! return symbol for current feature. Should not be used individually: there could be more symbols for a feature + virtual QgsSymbolV2* symbolForFeature( QgsFeature& feature ); + + virtual void renderFeature( QgsFeature& feature, QgsRenderContext& context, int layer = -1, bool selected = false, bool drawVertexMarker = false ); + + virtual void startRender( QgsRenderContext& context, const QgsVectorLayer *vlayer ); + + virtual void stopRender( QgsRenderContext& context ); + + virtual QList usedAttributes(); + + virtual QgsFeatureRendererV2* clone(); + + virtual QgsSymbolV2List symbols(); + + //! store renderer info to XML element + virtual QDomElement save( QDomDocument& doc ); + + ///// + + //! return the total number of rules + int ruleCount(); + //! get reference to rule at index (valid indexes: 0...count-1) + Rule& ruleAt( int index ); + //! add rule to the end of the list of rules + void addRule( const Rule& rule ); + //! insert rule to a specific position of the list of rules + void insertRule( int index, const Rule& rule ); + //! modify the rule at a specific position of the list of rules + void updateRuleAt( int index, const Rule& rule ); + //! remove the rule at the specified index + void removeRuleAt( int index ); + + ////// + + //! take a rule and create a list of new rules based on the categories from categorized symbol renderer + static QList refineRuleCategories( Rule& initialRule, QgsCategorizedSymbolRendererV2* r ); + //! take a rule and create a list of new rules based on the ranges from graduated symbol renderer + static QList refineRuleRanges( Rule& initialRule, QgsGraduatedSymbolRendererV2* r ); + //! take a rule and create a list of new rules with intervals of scales given by the passed scale denominators + static QList refineRuleScales( Rule& initialRule, QList scales ); + + protected: + //! the list of rules + QList mRules; + //! the default symbol, used for the first rule with no filter + QgsSymbolV2* mDefaultSymbol; + + // temporary + QList mCurrentRules; + QgsFieldMap mCurrentFields; + QgsSymbolV2* mCurrentSymbol; +}; + +#endif // QGSRULEBASEDRENDERERV2_H Index: src/core/symbology-ng/qgsrulebasedrendererv2.cpp =================================================================== --- src/core/symbology-ng/qgsrulebasedrendererv2.cpp (revision 0) +++ src/core/symbology-ng/qgsrulebasedrendererv2.cpp (revision 0) @@ -0,0 +1,368 @@ +/*************************************************************************** + qgsrulebasedrendererv2.cpp - Rule-based renderer (symbology-ng) + --------------------- + begin : May 2010 + copyright : (C) 2010 by Martin Dobias + email : wonder.sk at gmail.com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgsrulebasedrendererv2.h" + +#include "qgssearchtreenode.h" +#include "qgssymbollayerv2utils.h" +#include "qgsrendercontext.h" +#include "qgsvectorlayer.h" +#include "qgslogger.h" + +#include + +#include +#include + + + +QgsRuleBasedRendererV2::Rule::Rule( QgsSymbolV2* symbol, int scaleMinDenom, int scaleMaxDenom, QString filterExp ) + : mSymbol( symbol ), + mScaleMinDenom( scaleMinDenom ), mScaleMaxDenom( scaleMaxDenom ), + mFilterExp( filterExp ) +{ + initFilter(); +} + +QgsRuleBasedRendererV2::Rule::Rule( const QgsRuleBasedRendererV2::Rule& other ) + : mSymbol( NULL ) +{ + *this = other; +} + +QgsRuleBasedRendererV2::Rule::~Rule() +{ + delete mSymbol; +} + +void QgsRuleBasedRendererV2::Rule::initFilter() +{ + if ( !mFilterExp.isEmpty() ) + { + mFilterParsed.setString( mFilterExp ); + mFilterTree = mFilterParsed.tree(); // may be NULL if there's no input + } + else + { + mFilterTree = NULL; + } +} + +QString QgsRuleBasedRendererV2::Rule::dump() const +{ + return QString( "RULE - scale [%1,%2] - filter %3 - symbol %4" ) + .arg( mScaleMinDenom ).arg( mScaleMaxDenom ) + .arg( mFilterExp ).arg( mSymbol->dump() ); + +} + +QStringList QgsRuleBasedRendererV2::Rule::needsFields() const +{ + if ( ! mFilterTree ) + return QStringList(); + + return mFilterTree->referencedColumns(); +} + +bool QgsRuleBasedRendererV2::Rule::isFilterOK( const QgsFieldMap& fields, QgsFeature& f ) const +{ + if ( ! mFilterTree ) + return true; + + bool res = mFilterTree->checkAgainst( fields, f.attributeMap() ); + //print "is_ok", res, feature.id(), feature.attributeMap() + return res; +} + +bool QgsRuleBasedRendererV2::Rule::isScaleOK( double scale ) const +{ + if ( mScaleMinDenom == 0 && mScaleMaxDenom == 0 ) + return true; + if ( mScaleMinDenom != 0 && mScaleMinDenom > scale ) + return false; + if ( mScaleMaxDenom != 0 && mScaleMaxDenom < scale ) + return false; + return true; +} + +QgsRuleBasedRendererV2::Rule& QgsRuleBasedRendererV2::Rule::operator=( const QgsRuleBasedRendererV2::Rule & other ) +{ + if ( this != &other ) + { + delete mSymbol; + mSymbol = other.mSymbol->clone(); + + mScaleMinDenom = other.mScaleMinDenom; + mScaleMaxDenom = other.mScaleMaxDenom; + mFilterExp = other.mFilterExp; + initFilter(); + } + return *this; +} + +///////////////////// + +QgsRuleBasedRendererV2::QgsRuleBasedRendererV2( QgsSymbolV2* defaultSymbol ) + : QgsFeatureRendererV2( "RuleRenderer" ) +{ + mDefaultSymbol = defaultSymbol; + + // add the default rule + mRules << Rule( defaultSymbol->clone() ); +} + + +QgsSymbolV2* QgsRuleBasedRendererV2::symbolForFeature( QgsFeature& feature ) +{ + return mCurrentSymbol; +} + +void QgsRuleBasedRendererV2::renderFeature( QgsFeature& feature, + QgsRenderContext& context, + int layer, + bool selected, + bool drawVertexMarker ) +{ + for ( QList::iterator it = mCurrentRules.begin(); it != mCurrentRules.end(); ++it ) + { + Rule* rule = *it; + if ( rule->isFilterOK( mCurrentFields, feature ) ) + { + mCurrentSymbol = rule->symbol(); + // will ask for mCurrentSymbol + QgsFeatureRendererV2::renderFeature( feature, context, layer, selected, drawVertexMarker ); + } + } +} + + +void QgsRuleBasedRendererV2::startRender( QgsRenderContext& context, const QgsVectorLayer *vlayer ) +{ + double currentScale = context.rendererScale(); + // filter out rules which are not compatible with this scale + + mCurrentRules.clear(); + for ( QList::iterator it = mRules.begin(); it != mRules.end(); ++it ) + { + Rule& rule = *it; + if ( rule.isScaleOK( currentScale ) ) + mCurrentRules.append( &rule ); + } + + mCurrentFields = vlayer->pendingFields(); + + for ( QList::iterator it = mCurrentRules.begin(); it != mCurrentRules.end(); ++it ) + { + Rule* rule = *it; + rule->symbol()->startRender( context ); + } +} + +void QgsRuleBasedRendererV2::stopRender( QgsRenderContext& context ) +{ + for ( QList::iterator it = mCurrentRules.begin(); it != mCurrentRules.end(); ++it ) + { + Rule* rule = *it; + rule->symbol()->stopRender( context ); + } + + mCurrentRules.clear(); + mCurrentFields.clear(); +} + +QList QgsRuleBasedRendererV2::usedAttributes() +{ + QSet attrs; + for ( QList::iterator it = mRules.begin(); it != mRules.end(); ++it ) + { + Rule& rule = *it; + attrs.unite( rule.needsFields().toSet() ); + } + return attrs.values(); +} + +QgsFeatureRendererV2* QgsRuleBasedRendererV2::clone() +{ + QgsSymbolV2* s = mDefaultSymbol->clone(); + QgsRuleBasedRendererV2* r = new QgsRuleBasedRendererV2( s ); + r->mRules = mRules; + return r; +} + +QgsSymbolV2List QgsRuleBasedRendererV2::symbols() +{ + QgsSymbolV2List lst; + for ( QList::iterator it = mRules.begin(); it != mRules.end(); ++it ) + { + Rule& rule = *it; + lst.append( rule.symbol() ); + } + + return lst; +} + +QDomElement QgsRuleBasedRendererV2::save( QDomDocument& doc ) +{ + QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME ); + rendererElem.setAttribute( "type", "RuleRenderer" ); + + QDomElement rulesElem = doc.createElement( "rules" ); + + QgsSymbolV2Map symbols; + symbols["default"] = mDefaultSymbol; + + int i = 0; + for ( QList::iterator it = mRules.begin(); it != mRules.end(); ++i, ++it ) + { + Rule& rule = *it; + symbols[QString::number( i )] = rule.symbol(); + QDomElement ruleElem = doc.createElement( "rule" ); + ruleElem.setAttribute( "symbol", i ); + ruleElem.setAttribute( "filter", rule.filterExpression() ); + ruleElem.setAttribute( "scalemindenom", rule.scaleMinDenom() ); + ruleElem.setAttribute( "scalemaxdenom", rule.scaleMaxDenom() ); + rulesElem.appendChild( ruleElem ); + } + rendererElem.appendChild( rulesElem ); + + QDomElement symbolsElem = QgsSymbolLayerV2Utils::saveSymbols( symbols, "symbols", doc ); + rendererElem.appendChild( symbolsElem ); + + return rendererElem; +} + + +QgsFeatureRendererV2* QgsRuleBasedRendererV2::create( QDomElement& element ) +{ + // load symbols + QDomElement symbolsElem = element.firstChildElement( "symbols" ); + if ( symbolsElem.isNull() ) + return NULL; + + QgsSymbolV2Map symbolMap = QgsSymbolLayerV2Utils::loadSymbols( symbolsElem ); + + if ( !symbolMap.contains( "default" ) ) + { + QgsDebugMsg( "default symbol not found!" ); + return NULL; + } + + QgsRuleBasedRendererV2* r = new QgsRuleBasedRendererV2( symbolMap.take( "default" ) ); + r->mRules.clear(); + + QDomElement rulesElem = element.firstChildElement( "rules" ); + QDomElement ruleElem = rulesElem.firstChildElement( "rule" ); + while ( !ruleElem.isNull() ) + { + QString symbolIdx = ruleElem.attribute( "symbol" ); + if ( symbolMap.contains( symbolIdx ) ) + { + QString filterExp = ruleElem.attribute( "filter" ); + int scaleMinDenom = ruleElem.attribute( "scalemindenom", "0" ).toInt(); + int scaleMaxDenom = ruleElem.attribute( "scalemaxdenom", "0" ).toInt(); + r->mRules.append( Rule( symbolMap.take( symbolIdx ), scaleMinDenom, scaleMaxDenom, filterExp ) ); + } + else + { + QgsDebugMsg( "symbol for rule " + symbolIdx + " not found! (skipping)" ); + } + ruleElem = ruleElem.nextSiblingElement( "rule" ); + } + + // delete symbols if there are any more + QgsSymbolLayerV2Utils::clearSymbolMap( symbolMap ); + + return r; +} + + +int QgsRuleBasedRendererV2::ruleCount() +{ + return mRules.count(); +} + +QgsRuleBasedRendererV2::Rule& QgsRuleBasedRendererV2::ruleAt( int index ) +{ + return mRules[index]; +} + +void QgsRuleBasedRendererV2::addRule( const QgsRuleBasedRendererV2::Rule& rule ) +{ + mRules.append( rule ); +} + +void QgsRuleBasedRendererV2::insertRule( int index, const QgsRuleBasedRendererV2::Rule& rule ) +{ + mRules.insert( index, rule ); +} + +void QgsRuleBasedRendererV2::updateRuleAt( int index, const QgsRuleBasedRendererV2::Rule& rule ) +{ + mRules[index] = rule; +} + +void QgsRuleBasedRendererV2::removeRuleAt( int index ) +{ + mRules.removeAt( index ); +} + +#include "qgscategorizedsymbolrendererv2.h" +#include "qgsgraduatedsymbolrendererv2.h" + +QList QgsRuleBasedRendererV2::refineRuleCategories( QgsRuleBasedRendererV2::Rule& initialRule, QgsCategorizedSymbolRendererV2* r ) +{ + QList rules; + foreach( const QgsRendererCategoryV2& cat, r->categories() ) + { + QString newfilter = QString( "%1 = '%2'" ).arg( r->classAttribute() ).arg( cat.value().toString() ); + QString filter = initialRule.filterExpression(); + if ( filter.isEmpty() ) + filter = newfilter; + else + filter = QString( "(%1) AND (%2)" ).arg( filter ).arg( newfilter ); + rules.append( Rule( cat.symbol()->clone(), initialRule.scaleMinDenom(), initialRule.scaleMaxDenom(), filter ) ); + } + return rules; +} + +QList QgsRuleBasedRendererV2::refineRuleRanges( QgsRuleBasedRendererV2::Rule& initialRule, QgsGraduatedSymbolRendererV2* r ) +{ + QList rules; + foreach( const QgsRendererRangeV2& rng, r->ranges() ) + { + QString newfilter = QString( "%1 >= '%2' AND %1 <= '%3'" ).arg( r->classAttribute() ).arg( rng.lowerValue() ).arg( rng.upperValue() ); + QString filter = initialRule.filterExpression(); + if ( filter.isEmpty() ) + filter = newfilter; + else + filter = QString( "(%1) AND (%2)" ).arg( filter ).arg( newfilter ); + rules.append( Rule( rng.symbol()->clone(), initialRule.scaleMinDenom(), initialRule.scaleMaxDenom(), filter ) ); + } + return rules; +} + +QList QgsRuleBasedRendererV2::refineRuleScales( QgsRuleBasedRendererV2::Rule& initialRule, QList scales ) +{ + QList rules; + int oldScale = 0; + foreach( int scale, scales ) + { + rules.append( Rule( initialRule.symbol()->clone(), oldScale, scale, initialRule.filterExpression() ) ); + oldScale = scale; + } + // last rule + rules.append( Rule( initialRule.symbol()->clone(), oldScale, 0, initialRule.filterExpression() ) ); + return rules; +} Index: src/core/CMakeLists.txt =================================================================== --- src/core/CMakeLists.txt (revision 13699) +++ src/core/CMakeLists.txt (working copy) @@ -32,6 +32,7 @@ symbology-ng/qgssinglesymbolrendererv2.cpp symbology-ng/qgscategorizedsymbolrendererv2.cpp symbology-ng/qgsgraduatedsymbolrendererv2.cpp + symbology-ng/qgsrulebasedrendererv2.cpp symbology-ng/qgsvectorcolorrampv2.cpp symbology-ng/qgsstylev2.cpp symbology-ng/qgssymbologyv2conversion.cpp Index: src/ui/qgsrulebasedrendererv2widget.ui =================================================================== --- src/ui/qgsrulebasedrendererv2widget.ui (revision 0) +++ src/ui/qgsrulebasedrendererv2widget.ui (revision 0) @@ -0,0 +1,115 @@ + + + QgsRuleBasedRendererV2Widget + + + + 0 + 0 + 460 + 221 + + + + Form + + + + + + Rules: + + + + + + + false + + + true + + + + Rule + + + + + + + + + + Add + + + + + + + Refine + + + + + + + Edit + + + + + + + Remove + + + + + + + + + Rule grouping + + + + + + No grouping + + + true + + + + + + + Group by filter + + + + + + + Group by scale + + + + + + + + + + + QgsRendererRulesTreeWidget + QTreeWidget +
qgsrulebasedrendererv2widget.h
+
+
+ + +
Index: src/ui/qgsrendererrulepropsdialogbase.ui =================================================================== --- src/ui/qgsrendererrulepropsdialogbase.ui (revision 0) +++ src/ui/qgsrendererrulepropsdialogbase.ui (revision 0) @@ -0,0 +1,171 @@ + + + QgsRendererRulePropsDialog + + + + 0 + 0 + 547 + 300 + + + + Rule properties + + + + + + + + Filter + + + + + + + + + + Test + + + + + + + + + Scale range + + + true + + + false + + + + + + Min. scale + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + 1 : + + + 0 + + + 1000000000 + + + 1000 + + + 1000 + + + + + + + Max. scale + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + 1 : + + + 0 + + + 1000000000 + + + 1000 + + + 1000 + + + + + + + + + + + 0 + 0 + + + + Symbol + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + QgsRendererRulePropsDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + QgsRendererRulePropsDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + Index: python/analysis/qgsoverlayanalyzer.sip =================================================================== --- python/analysis/qgsoverlayanalyzer.sip (revision 13699) +++ python/analysis/qgsoverlayanalyzer.sip (working copy) @@ -1,5 +1,3 @@ -/** polyline is just a list of points */ -typedef QMap QgsFieldMap; /** \ingroup analysis * The QGis class provides vector geometry analysis functions Index: python/core/symbology-ng-core.sip =================================================================== --- python/core/symbology-ng-core.sip (revision 13699) +++ python/core/symbology-ng-core.sip (working copy) @@ -51,6 +51,8 @@ sipClass = sipClass_QgsCategorizedSymbolRendererV2; else if (sipCpp->type() == "graduatedSymbol") sipClass = sipClass_QgsGraduatedSymbolRendererV2; + else if (sipCpp->type() == "RuleRenderer") + sipClass = sipClass_QgsRuleBasedRendererV2; else sipClass = 0; %End @@ -350,6 +352,101 @@ QgsSymbolV2* symbolForValue(double value); }; +/////////////// + +class QgsRuleBasedRendererV2 : QgsFeatureRendererV2 +{ +%TypeHeaderCode +#include +%End + + public: + + /** + This class keeps data about a rules for rule-based renderer. + A rule consists of a symbol, filter expression and range of scales. + If filter is empty, it matches all features. + If scale range has both values zero, it matches all scales. + If one of the min/max scale denominators is zero, there is no lower/upper bound for scales. + A rule matches if both filter and scale range match. + */ + class Rule + { + public: + //! Constructor takes ownership of the symbol + Rule( QgsSymbolV2* symbol /Transfer/, int scaleMinDenom = 0, int scaleMaxDenom = 0, QString filterExp = QString() ); + Rule( const QgsRuleBasedRendererV2::Rule& other ); + ~Rule(); + QString dump() const; + QStringList needsFields() const; + bool isFilterOK( const QgsFieldMap& fields, QgsFeature& f ) const; + bool isScaleOK( double scale ) const; + + QgsSymbolV2* symbol(); + bool dependsOnScale() const; + int scaleMinDenom() const; + int scaleMaxDenom() const; + QString filterExpression() const; + + void setScaleMinDenom( int scaleMinDenom ); + void setScaleMaxDenom( int scaleMaxDenom ); + void setFilterExpression( QString filterExp ); + + //Rule& operator=( const Rule& other ); + }; + + ///// + + static QgsFeatureRendererV2* create( QDomElement& element ) /Factory/; + + //! Constructor. Takes ownership of the defult symbol. + QgsRuleBasedRendererV2( QgsSymbolV2* defaultSymbol /Transfer/ ); + + //! return symbol for current feature. Should not be used individually: there could be more symbols for a feature + virtual QgsSymbolV2* symbolForFeature( QgsFeature& feature ); + + virtual void renderFeature( QgsFeature& feature, QgsRenderContext& context, int layer = -1, bool selected = false, bool drawVertexMarker = false ); + + virtual void startRender( QgsRenderContext& context, const QgsVectorLayer *vlayer ); + + virtual void stopRender( QgsRenderContext& context ); + + virtual QList usedAttributes(); + + virtual QgsFeatureRendererV2* clone() /Factory/; + + virtual QgsSymbolV2List symbols(); + + //! store renderer info to XML element + virtual QDomElement save( QDomDocument& doc ); + + ///// + + //! return the total number of rules + int ruleCount(); + //! get reference to rule at index (valid indexes: 0...count-1) + QgsRuleBasedRendererV2::Rule& ruleAt( int index ); + //! add rule to the end of the list of rules + void addRule( const QgsRuleBasedRendererV2::Rule& rule ); + //! insert rule to a specific position of the list of rules + void insertRule( int index, const QgsRuleBasedRendererV2::Rule& rule ); + //! modify the rule at a specific position of the list of rules + void updateRuleAt( int index, const QgsRuleBasedRendererV2::Rule& rule ); + //! remove the rule at the specified index + void removeRuleAt( int index ); + + ////// + + //! take a rule and create a list of new rules based on the categories from categorized symbol renderer + static QList refineRuleCategories( QgsRuleBasedRendererV2::Rule& initialRule, QgsCategorizedSymbolRendererV2* r ); + //! take a rule and create a list of new rules based on the ranges from graduated symbol renderer + static QList refineRuleRanges( QgsRuleBasedRendererV2::Rule& initialRule, QgsGraduatedSymbolRendererV2* r ); + //! take a rule and create a list of new rules with intervals of scales given by the passed scale denominators + static QList refineRuleScales( QgsRuleBasedRendererV2::Rule& initialRule, QList scales ); + +}; + + ////////// class QgsSymbolLayerV2 @@ -663,6 +760,8 @@ typedef QMap QgsStringMap; +typedef QMap QgsFieldMap; + ////////// class QgsSymbolLayerV2Widget /External/;