Skip to content

Commit

Permalink
Unifying rule based renderer scale API, fix scale related bugs
Browse files Browse the repository at this point in the history
Fix #15512
  • Loading branch information
nyalldawson committed Jun 9, 2017
1 parent 37f86f5 commit 9805782
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 74 deletions.
1 change: 1 addition & 0 deletions doc/api_break.dox
Expand Up @@ -2004,6 +2004,7 @@ QgsRuleBasedRenderer {#qgis_api_break_3_0_QgsRuleBasedRenderer}

- QgsRuleBasedRenderer.Rule checkState() and setCheckState() were removed. Use active() and setActive() instead.
- QgsRuleBasedRenderer.Rule updateElseRules() was removed.
- QgsRuleBasedRenderer.Rule scaleMinDenom(), scaleMaxDenom(), setScaleMaxDenom() and setScaleMinDenom() were removed. Use minimumScale(), maximumScale(), setMinimumScale() and setMaximumScale() instead.
- startRender( QgsRenderContext& context, const QgsFields& fields ) was removed. Use startRender( QgsRenderContext& context, const QgsFields& fields, QString& filter ) instead.


Expand Down
51 changes: 34 additions & 17 deletions python/core/symbology-ng/qgsrulebasedrenderer.sip
Expand Up @@ -81,7 +81,7 @@ A rule matches if both filter and scale range match.
Rendered
};

Rule( QgsSymbol *symbol /Transfer/, int scaleMinDenom = 0, int scaleMaxDenom = 0, const QString &filterExp = QString(),
Rule( QgsSymbol *symbol /Transfer/, int maximumScale = 0, int minimumScale = 0, const QString &filterExp = QString(),
const QString &label = QString(), const QString &description = QString(), bool elseRule = false );
%Docstring
Constructor takes ownership of the symbol
Expand Down Expand Up @@ -137,8 +137,9 @@ Constructor takes ownership of the symbol

bool isScaleOK( double scale ) const;
%Docstring
Check if this rule applies for a given scale
\param scale The scale to check. If set to 0, it will always return true.
Check if this rule applies for a given ``scale``.
The ``scale`` value indicates the scale denominator, e.g. 1000.0 for a 1:1000 map.
If set to 0, it will always return true.

:return: If the rule will be evaluated at this scale
:rtype: bool
Expand All @@ -156,13 +157,27 @@ Constructor takes ownership of the symbol
%Docstring
:rtype: bool
%End
int scaleMinDenom() const;

double maximumScale() const;
%Docstring
:rtype: int
Returns the maximum map scale (i.e. most "zoomed in" scale) at which the rule will be active.
The scale value indicates the scale denominator, e.g. 1000.0 for a 1:1000 map.
A scale of 0 indicates no maximum scale visibility.
.. seealso:: minimumScale()
.. seealso:: setMaximumScale()
.. versionadded:: 3.0
:rtype: float
%End
int scaleMaxDenom() const;

double minimumScale() const;
%Docstring
:rtype: int
Returns the minimum map scale (i.e. most "zoomed out" scale) at which the rule will be active.
The scale value indicates the scale denominator, e.g. 1000.0 for a 1:1000 map.
A scale of 0 indicates no minimum scale visibility.
.. seealso:: maximumScale()
.. seealso:: setMinimumScale()
.. versionadded:: 3.0
:rtype: float
%End

QgsExpression *filter() const;
Expand Down Expand Up @@ -211,20 +226,22 @@ set a new symbol (or NULL). Deletes old symbol.
%End
void setLabel( const QString &label );

void setScaleMinDenom( int scaleMinDenom );
void setMinimumScale( double scale );
%Docstring
Set the minimum denominator for which this rule shall apply.
E.g. 1000 if it shall be evaluated between 1:1000 and 1:100'000
Set to 0 to disable the minimum check
\param scaleMinDenom The minimum scale denominator for this rule
Sets the minimum map ``scale`` (i.e. most "zoomed out" scale) at which the rule will be active.
The ``scale`` value indicates the scale denominator, e.g. 1000.0 for a 1:1000 map.
A ``scale`` of 0 indicates no minimum scale visibility.
.. seealso:: minimumScale()
.. seealso:: setMaximumScale()
%End

void setScaleMaxDenom( int scaleMaxDenom );
void setMaximumScale( double scale );
%Docstring
Set the maximum denominator for which this rule shall apply.
E.g. 100'000 if it shall be evaluated between 1:1000 and 1:100'000
Set to 0 to disable the maximum check
\param scaleMaxDenom maximum scale denominator for this rule
Sets the maximum map ``scale`` (i.e. most "zoomed in" scale) at which the rule will be active.
The ``scale`` value indicates the scale denominator, e.g. 1000.0 for a 1:1000 map.
A ``scale`` of 0 indicates no maximum scale visibility.
.. seealso:: maximumScale()
.. seealso:: setMinimumScale()
%End

void setFilterExpression( const QString &filterExp );
Expand Down
32 changes: 16 additions & 16 deletions src/core/symbology-ng/qgsrulebasedrenderer.cpp
Expand Up @@ -38,8 +38,8 @@
QgsRuleBasedRenderer::Rule::Rule( QgsSymbol *symbol, int scaleMinDenom, int scaleMaxDenom, const QString &filterExp, const QString &label, const QString &description, bool elseRule )
: mParent( nullptr )
, mSymbol( symbol )
, mScaleMinDenom( scaleMinDenom )
, mScaleMaxDenom( scaleMaxDenom )
, mMaximumScale( scaleMinDenom )
, mMinimumScale( scaleMaxDenom )
, mFilterExp( filterExp )
, mLabel( label )
, mDescription( description )
Expand Down Expand Up @@ -168,7 +168,7 @@ QString QgsRuleBasedRenderer::Rule::dump( int indent ) const
off.fill( QChar( ' ' ), indent );
QString symbolDump = ( mSymbol ? mSymbol->dump() : QStringLiteral( "[]" ) );
QString msg = off + QStringLiteral( "RULE %1 - scale [%2,%3] - filter %4 - symbol %5\n" )
.arg( mLabel ).arg( mScaleMinDenom ).arg( mScaleMaxDenom )
.arg( mLabel ).arg( mMaximumScale ).arg( mMinimumScale )
.arg( mFilterExp, symbolDump );

QStringList lst;
Expand Down Expand Up @@ -257,7 +257,7 @@ QgsLegendSymbolListV2 QgsRuleBasedRenderer::Rule::legendSymbolItemsV2( int curre
QgsLegendSymbolListV2 lst;
if ( currentLevel != -1 ) // root rule should not be shown
{
lst << QgsLegendSymbolItem( mSymbol, mLabel, mRuleKey, true, mScaleMinDenom, mScaleMaxDenom, currentLevel, mParent ? mParent->mRuleKey : QString() );
lst << QgsLegendSymbolItem( mSymbol, mLabel, mRuleKey, true, mMaximumScale, mMinimumScale, currentLevel, mParent ? mParent->mRuleKey : QString() );
}

for ( RuleList::const_iterator it = mChildren.constBegin(); it != mChildren.constEnd(); ++it )
Expand All @@ -283,19 +283,19 @@ bool QgsRuleBasedRenderer::Rule::isScaleOK( double scale ) const
{
if ( qgsDoubleNear( scale, 0.0 ) ) // so that we can count features in classes without scale context
return true;
if ( mScaleMinDenom == 0 && mScaleMaxDenom == 0 )
if ( mMaximumScale == 0 && mMinimumScale == 0 )
return true;
if ( mScaleMinDenom != 0 && mScaleMinDenom > scale )
if ( mMaximumScale != 0 && mMaximumScale > scale )
return false;
if ( mScaleMaxDenom != 0 && mScaleMaxDenom < scale )
if ( mMinimumScale != 0 && mMinimumScale < scale )
return false;
return true;
}

QgsRuleBasedRenderer::Rule *QgsRuleBasedRenderer::Rule::clone() const
{
QgsSymbol *sym = mSymbol ? mSymbol->clone() : nullptr;
Rule *newrule = new Rule( sym, mScaleMinDenom, mScaleMaxDenom, mFilterExp, mLabel, mDescription );
Rule *newrule = new Rule( sym, mMaximumScale, mMinimumScale, mFilterExp, mLabel, mDescription );
newrule->setActive( mIsActive );
// clone children
Q_FOREACH ( Rule *rule, mChildren )
Expand All @@ -315,10 +315,10 @@ QDomElement QgsRuleBasedRenderer::Rule::save( QDomDocument &doc, QgsSymbolMap &s
}
if ( !mFilterExp.isEmpty() )
ruleElem.setAttribute( QStringLiteral( "filter" ), mFilterExp );
if ( mScaleMinDenom != 0 )
ruleElem.setAttribute( QStringLiteral( "scalemindenom" ), mScaleMinDenom );
if ( mScaleMaxDenom != 0 )
ruleElem.setAttribute( QStringLiteral( "scalemaxdenom" ), mScaleMaxDenom );
if ( mMaximumScale != 0 )
ruleElem.setAttribute( QStringLiteral( "scalemindenom" ), mMaximumScale );
if ( mMinimumScale != 0 )
ruleElem.setAttribute( QStringLiteral( "scalemaxdenom" ), mMinimumScale );
if ( !mLabel.isEmpty() )
ruleElem.setAttribute( QStringLiteral( "label" ), mLabel );
if ( !mDescription.isEmpty() )
Expand Down Expand Up @@ -348,7 +348,7 @@ void QgsRuleBasedRenderer::Rule::toSld( QDomDocument &doc, QDomElement &element,
props[ QStringLiteral( "filter" )] += mFilterExp;
}

QgsSymbolLayerUtils::mergeScaleDependencies( mScaleMinDenom, mScaleMaxDenom, props );
QgsSymbolLayerUtils::mergeScaleDependencies( mMaximumScale, mMinimumScale, props );

if ( mSymbol )
{
Expand Down Expand Up @@ -1153,12 +1153,12 @@ void QgsRuleBasedRenderer::refineRuleRanges( QgsRuleBasedRenderer::Rule *initial
void QgsRuleBasedRenderer::refineRuleScales( QgsRuleBasedRenderer::Rule *initialRule, QList<int> scales )
{
std::sort( scales.begin(), scales.end() ); // make sure the scales are in ascending order
int oldScale = initialRule->scaleMinDenom();
int maxDenom = initialRule->scaleMaxDenom();
double oldScale = initialRule->maximumScale();
double maxDenom = initialRule->minimumScale();
QgsSymbol *symbol = initialRule->symbol();
Q_FOREACH ( int scale, scales )
{
if ( initialRule->scaleMinDenom() >= scale )
if ( initialRule->maximumScale() >= scale )
continue; // jump over the first scales out of the interval
if ( maxDenom != 0 && maxDenom <= scale )
break; // ignore the latter scales out of the interval
Expand Down
56 changes: 39 additions & 17 deletions src/core/symbology-ng/qgsrulebasedrenderer.h
Expand Up @@ -125,7 +125,7 @@ class CORE_EXPORT QgsRuleBasedRenderer : public QgsFeatureRenderer
};

//! Constructor takes ownership of the symbol
Rule( QgsSymbol *symbol SIP_TRANSFER, int scaleMinDenom = 0, int scaleMaxDenom = 0, const QString &filterExp = QString(),
Rule( QgsSymbol *symbol SIP_TRANSFER, int maximumScale = 0, int minimumScale = 0, const QString &filterExp = QString(),
const QString &label = QString(), const QString &description = QString(), bool elseRule = false );
~Rule();

Expand Down Expand Up @@ -171,18 +171,37 @@ class CORE_EXPORT QgsRuleBasedRenderer : public QgsFeatureRenderer
bool isFilterOK( QgsFeature &f, QgsRenderContext *context = nullptr ) const;

/**
* Check if this rule applies for a given scale
* \param scale The scale to check. If set to 0, it will always return true.
* Check if this rule applies for a given \a scale.
* The \a scale value indicates the scale denominator, e.g. 1000.0 for a 1:1000 map.
* If set to 0, it will always return true.
*
* \returns If the rule will be evaluated at this scale
*/
bool isScaleOK( double scale ) const;

QgsSymbol *symbol() { return mSymbol; }
QString label() const { return mLabel; }
bool dependsOnScale() const { return mScaleMinDenom != 0 || mScaleMaxDenom != 0; }
int scaleMinDenom() const { return mScaleMinDenom; }
int scaleMaxDenom() const { return mScaleMaxDenom; }
bool dependsOnScale() const { return mMaximumScale != 0 || mMinimumScale != 0; }

/**
* Returns the maximum map scale (i.e. most "zoomed in" scale) at which the rule will be active.
* The scale value indicates the scale denominator, e.g. 1000.0 for a 1:1000 map.
* A scale of 0 indicates no maximum scale visibility.
* \see minimumScale()
* \see setMaximumScale()
* \since QGIS 3.0
*/
double maximumScale() const { return mMaximumScale; }

/**
* Returns the minimum map scale (i.e. most "zoomed out" scale) at which the rule will be active.
* The scale value indicates the scale denominator, e.g. 1000.0 for a 1:1000 map.
* A scale of 0 indicates no minimum scale visibility.
* \see maximumScale()
* \see setMinimumScale()
* \since QGIS 3.0
*/
double minimumScale() const { return mMinimumScale; }

/**
* A filter that will check if this rule applies
Expand Down Expand Up @@ -222,20 +241,22 @@ class CORE_EXPORT QgsRuleBasedRenderer : public QgsFeatureRenderer
void setLabel( const QString &label ) { mLabel = label; }

/**
* Set the minimum denominator for which this rule shall apply.
* E.g. 1000 if it shall be evaluated between 1:1000 and 1:100'000
* Set to 0 to disable the minimum check
* \param scaleMinDenom The minimum scale denominator for this rule
* Sets the minimum map \a scale (i.e. most "zoomed out" scale) at which the rule will be active.
* The \a scale value indicates the scale denominator, e.g. 1000.0 for a 1:1000 map.
* A \a scale of 0 indicates no minimum scale visibility.
* \see minimumScale()
* \see setMaximumScale()
*/
void setScaleMinDenom( int scaleMinDenom ) { mScaleMinDenom = scaleMinDenom; }
void setMinimumScale( double scale ) { mMinimumScale = scale; }

/**
* Set the maximum denominator for which this rule shall apply.
* E.g. 100'000 if it shall be evaluated between 1:1000 and 1:100'000
* Set to 0 to disable the maximum check
* \param scaleMaxDenom maximum scale denominator for this rule
* Sets the maximum map \a scale (i.e. most "zoomed in" scale) at which the rule will be active.
* The \a scale value indicates the scale denominator, e.g. 1000.0 for a 1:1000 map.
* A \a scale of 0 indicates no maximum scale visibility.
* \see maximumScale()
* \see setMinimumScale()
*/
void setScaleMaxDenom( int scaleMaxDenom ) { mScaleMaxDenom = scaleMaxDenom; }
void setMaximumScale( double scale ) { mMaximumScale = scale; }

/**
* Set the expression used to check if a given feature shall be rendered with this rule
Expand Down Expand Up @@ -383,7 +404,8 @@ class CORE_EXPORT QgsRuleBasedRenderer : public QgsFeatureRenderer

Rule *mParent; // parent rule (NULL only for root rule)
QgsSymbol *mSymbol = nullptr;
int mScaleMinDenom, mScaleMaxDenom;
double mMaximumScale = 0;
double mMinimumScale = 0;
QString mFilterExp, mLabel, mDescription;
bool mElseRule;
RuleList mChildren;
Expand Down
34 changes: 10 additions & 24 deletions src/gui/symbology-ng/qgsrulebasedrendererwidget.cpp
Expand Up @@ -621,11 +621,8 @@ QgsRendererRulePropsWidget::QgsRendererRulePropsWidget( QgsRuleBasedRenderer::Ru
if ( mRule->dependsOnScale() )
{
groupScale->setChecked( true );
// caution: rule uses scale denom, scale widget uses true scales
if ( rule->scaleMinDenom() > 0 )
mScaleRangeWidget->setMaximumScale( rule->scaleMinDenom() );
if ( rule->scaleMaxDenom() > 0 )
mScaleRangeWidget->setMinimumScale( rule->scaleMaxDenom() );
mScaleRangeWidget->setMaximumScale( qMax( rule->maximumScale(), 0.0 ) );
mScaleRangeWidget->setMinimumScale( qMax( rule->minimumScale(), 0.0 ) );
}
mScaleRangeWidget->setMapCanvas( mContext.mapCanvas() );

Expand Down Expand Up @@ -770,8 +767,8 @@ void QgsRendererRulePropsWidget::apply()
mRule->setLabel( editLabel->text() );
mRule->setDescription( editDescription->text() );
// caution: rule uses scale denom, scale widget uses true scales
mRule->setScaleMinDenom( groupScale->isChecked() ? mScaleRangeWidget->minimumScale() : 0 );
mRule->setScaleMaxDenom( groupScale->isChecked() ? mScaleRangeWidget->maximumScale() : 0 );
mRule->setMinimumScale( groupScale->isChecked() ? mScaleRangeWidget->minimumScale() : 0 );
mRule->setMaximumScale( groupScale->isChecked() ? mScaleRangeWidget->maximumScale() : 0 );
mRule->setSymbol( groupSymbol->isChecked() ? mSymbol->clone() : nullptr );
}

Expand All @@ -790,17 +787,6 @@ void QgsRendererRulePropsWidget::setDockMode( bool dockMode )
setDragDropMode(QAbstractItemView::InternalMove);
*/

static QString _formatScale( int denom )
{
if ( denom != 0 )
{
QString txt = QStringLiteral( "1:%L1" ).arg( denom );
return txt;
}
else
return QString();
}

/////

QgsRuleBasedRendererModel::QgsRuleBasedRendererModel( QgsRuleBasedRenderer *r )
Expand Down Expand Up @@ -846,9 +832,9 @@ QVariant QgsRuleBasedRendererModel::data( const QModelIndex &index, int role ) c
return rule->filterExpression().isEmpty() ? tr( "(no filter)" ) : rule->filterExpression();
}
case 2:
return rule->dependsOnScale() ? _formatScale( rule->scaleMaxDenom() ) : QVariant();
return rule->dependsOnScale() ? QgsScaleComboBox::toString( rule->minimumScale() ) : QVariant();
case 3:
return rule->dependsOnScale() ? _formatScale( rule->scaleMinDenom() ) : QVariant();
return rule->dependsOnScale() ? QgsScaleComboBox::toString( rule->maximumScale() ) : QVariant();
case 4:
if ( mFeatureCountMap.count( rule ) == 1 )
{
Expand Down Expand Up @@ -913,9 +899,9 @@ QVariant QgsRuleBasedRendererModel::data( const QModelIndex &index, int role ) c
case 1:
return rule->filterExpression();
case 2:
return rule->scaleMaxDenom();
return rule->minimumScale();
case 3:
return rule->scaleMinDenom();
return rule->maximumScale();
default:
return QVariant();
}
Expand Down Expand Up @@ -1022,10 +1008,10 @@ bool QgsRuleBasedRendererModel::setData( const QModelIndex &index, const QVarian
rule->setFilterExpression( value.toString() );
break;
case 2: // scale min
rule->setScaleMaxDenom( value.toInt() );
rule->setMinimumScale( value.toDouble() );
break;
case 3: // scale max
rule->setScaleMinDenom( value.toInt() );
rule->setMaximumScale( value.toDouble() );
break;
default:
return false;
Expand Down

0 comments on commit 9805782

Please sign in to comment.