36#include <QDomDocument>
44 , mMaximumScale( scaleMinDenom )
45 , mMinimumScale( scaleMaxDenom )
46 , mFilterExp( filterExp )
48 , mDescription( description )
49 , mElseRule( elseRule )
52 mFilterExp = QStringLiteral(
"ELSE" );
54 mRuleKey = QUuid::createUuid().toString();
60 qDeleteAll( mChildren );
66 if ( mFilterExp.trimmed().compare( QLatin1String(
"ELSE" ), Qt::CaseInsensitive ) == 0 )
71 else if ( mFilterExp.trimmed().isEmpty() )
79 mFilter = std::make_unique< QgsExpression >( mFilterExp );
85 mChildren.append( rule );
92 mChildren.insert( i, rule );
99 mChildren.removeAll( rule );
106 delete mChildren.takeAt( i );
112 mChildren.removeAll( rule );
113 rule->mParent =
nullptr;
120 Rule *rule = mChildren.takeAt( i );
121 rule->mParent =
nullptr;
130 if ( key == mRuleKey )
133 const auto constMChildren = mChildren;
134 for (
Rule *rule : constMChildren )
143void QgsRuleBasedRenderer::Rule::updateElseRules()
146 const auto constMChildren = mChildren;
147 for (
Rule *rule : constMChildren )
149 if ( rule->isElse() )
156 mFilterExp = QStringLiteral(
"ELSE" );
174 if ( !mChildren.empty() )
176 for (
const Rule *rule : mChildren )
179 if ( !rule->accept( visitor ) )
193 off.fill( QChar(
' ' ), indent );
194 QString symbolDump = ( mSymbol ? mSymbol->dump() : QStringLiteral(
"[]" ) );
195 QString msg = off + QStringLiteral(
"RULE %1 - scale [%2,%3] - filter %4 - symbol %5\n" )
196 .arg( mLabel ).arg( mMaximumScale ).arg( mMinimumScale )
197 .arg( mFilterExp, symbolDump );
200 const auto constMChildren = mChildren;
201 for (
Rule *rule : constMChildren )
203 lst.append( rule->dump( indent + 2 ) );
205 msg += lst.join( QLatin1Char(
'\n' ) );
214 attrs.unite(
mFilter->referencedColumns() );
216 attrs.unite( mSymbol->usedAttributes( context ) );
219 const auto constMChildren = mChildren;
220 for (
Rule *rule : constMChildren )
222 attrs.unite( rule->usedAttributes( context ) );
232 const auto constMChildren = mChildren;
233 for (
Rule *rule : constMChildren )
235 if ( rule->needsGeometry() )
246 lst.append( mSymbol.get() );
248 const auto constMChildren = mChildren;
249 for (
Rule *rule : constMChildren )
251 lst += rule->symbols( context );
258 mSymbol.reset( sym );
263 mFilterExp = filterExp;
270 if ( currentLevel != -1 )
272 lst <<
QgsLegendSymbolItem( mSymbol.get(), mLabel, mRuleKey,
true, mMaximumScale, mMinimumScale, currentLevel, mParent ? mParent->mRuleKey : QString() );
275 for ( RuleList::const_iterator it = mChildren.constBegin(); it != mChildren.constEnd(); ++it )
286 if ( !
mFilter || mElseRule || ! context )
300 if ( !
qgsDoubleNear( mMaximumScale, 0.0 ) && mMaximumScale > scale )
302 if ( !
qgsDoubleNear( mMinimumScale, 0.0 ) && mMinimumScale < scale )
310 Rule *newrule =
new Rule( sym, mMaximumScale, mMinimumScale, mFilterExp, mLabel, mDescription );
313 const auto constMChildren = mChildren;
314 for (
Rule *rule : constMChildren )
321 QDomElement ruleElem = doc.createElement( QStringLiteral(
"rule" ) );
325 int symbolIndex = symbolMap.size();
326 symbolMap[QString::number( symbolIndex )] = mSymbol.get();
327 ruleElem.setAttribute( QStringLiteral(
"symbol" ), symbolIndex );
329 if ( !mFilterExp.isEmpty() )
330 ruleElem.setAttribute( QStringLiteral(
"filter" ), mFilterExp );
331 if ( mMaximumScale != 0 )
332 ruleElem.setAttribute( QStringLiteral(
"scalemindenom" ), mMaximumScale );
333 if ( mMinimumScale != 0 )
334 ruleElem.setAttribute( QStringLiteral(
"scalemaxdenom" ), mMinimumScale );
335 if ( !mLabel.isEmpty() )
336 ruleElem.setAttribute( QStringLiteral(
"label" ), mLabel );
337 if ( !mDescription.isEmpty() )
338 ruleElem.setAttribute( QStringLiteral(
"description" ), mDescription );
340 ruleElem.setAttribute( QStringLiteral(
"checkstate" ), 0 );
341 ruleElem.setAttribute( QStringLiteral(
"key" ), mRuleKey );
343 const auto constMChildren = mChildren;
344 for (
Rule *rule : constMChildren )
346 ruleElem.
appendChild( rule->save( doc, symbolMap ) );
355 if (
symbols( context ).isEmpty() )
358 if ( !mFilterExp.isEmpty() )
360 QString
filter = props.value( QStringLiteral(
"filter" ), QString() ).toString();
362 filter += QLatin1String(
" AND " );
364 props[ QStringLiteral(
"filter" )] =
filter;
371 QDomElement ruleElem = doc.createElement( QStringLiteral(
"se:Rule" ) );
372 element.appendChild( ruleElem );
376 QDomElement nameElem = doc.createElement( QStringLiteral(
"se:Name" ) );
377 nameElem.appendChild( doc.createTextNode( mLabel ) );
378 ruleElem.appendChild( nameElem );
380 if ( !mLabel.isEmpty() || !mDescription.isEmpty() )
382 QDomElement descrElem = doc.createElement( QStringLiteral(
"se:Description" ) );
383 if ( !mLabel.isEmpty() )
385 QDomElement titleElem = doc.createElement( QStringLiteral(
"se:Title" ) );
386 titleElem.appendChild( doc.createTextNode( mLabel ) );
387 descrElem.appendChild( titleElem );
389 if ( !mDescription.isEmpty() )
391 QDomElement abstractElem = doc.createElement( QStringLiteral(
"se:Abstract" ) );
392 abstractElem.appendChild( doc.createTextNode( mDescription ) );
393 descrElem.appendChild( abstractElem );
395 ruleElem.appendChild( descrElem );
398 if ( !props.value( QStringLiteral(
"filter" ), QString() ).toString().isEmpty() )
405 mSymbol->toSld( doc, ruleElem, props );
409 const auto constMChildren = mChildren;
410 for (
Rule *rule : constMChildren )
412 rule->toSld( doc, element, props );
418 mActiveChildren.clear();
431 mSymbol->startRender( context, fields );
435 QStringList subfilters;
436 const auto constMChildren = mChildren;
437 for (
Rule *rule : constMChildren )
440 if ( rule->startRender( context, fields, subfilter ) )
443 mActiveChildren.append( rule );
444 subfilters.append( subfilter );
452 if ( subfilters.length() > 1 || !subfilters.value( 0 ).isEmpty() )
454 if ( subfilters.contains( QStringLiteral(
"TRUE" ) ) )
456 sf = QStringLiteral(
"TRUE" );
467 else if ( subfilters.count() > 50 )
469 std::function<QString(
const QStringList & )>bt = [ &bt ](
const QStringList & subf )
471 if ( subf.count( ) == 1 )
475 else if ( subf.count( ) == 2 )
477 return subf.join( QLatin1String(
") OR (" ) ).prepend(
'(' ).append(
')' );
481 int midpos =
static_cast<int>( subf.length() / 2 );
482 return QStringLiteral(
"(%1) OR (%2)" ).arg( bt( subf.mid( 0, midpos ) ), bt( subf.mid( midpos ) ) );
485 sf = bt( subfilters );
489 sf = subfilters.join( QLatin1String(
") OR (" ) ).prepend(
'(' ).append(
')' );
501 if ( mSymbol || sf.isEmpty() )
502 filter = QStringLiteral(
"TRUE" );
508 else if ( !mFilterExp.trimmed().isEmpty() && !sf.isEmpty() )
509 filter = QStringLiteral(
"(%1) AND (%2)" ).arg( mFilterExp, sf );
510 else if ( !mFilterExp.trimmed().isEmpty() )
512 else if ( sf.isEmpty() )
513 filter = QStringLiteral(
"TRUE" );
524 return !mActiveChildren.empty();
529 QSet<int> symbolZLevelsSet;
535 for (
int i = 0; i < mSymbol->symbolLayerCount(); i++ )
537 symbolZLevelsSet.insert( mSymbol->symbolLayer( i )->renderingPass() );
542 QList<Rule *>::iterator it;
543 for ( it = mActiveChildren.begin(); it != mActiveChildren.end(); ++it )
548 return symbolZLevelsSet;
555 for (
int i = 0; i < mSymbol->symbolLayerCount(); i++ )
557 int normLevel = zLevelsToNormLevels.value( mSymbol->symbolLayer( i )->renderingPass() );
558 mSymbolNormZLevels.insert( normLevel );
563 const auto constMActiveChildren = mActiveChildren;
564 for (
Rule *rule : constMActiveChildren )
573 if ( !isFilterOK( featToRender.
feat, &context ) )
576 bool rendered =
false;
579 if ( mSymbol && mIsActive )
582 const auto constMSymbolNormZLevels = mSymbolNormZLevels;
583 for (
int normZLevel : constMSymbolNormZLevels )
586 renderQueue[normZLevel].jobs.append(
new RenderJob( featToRender, mSymbol.get() ) );
591 bool matchedAChild =
false;
594 const auto constMChildren = mChildren;
595 for (
Rule *rule : constMChildren )
598 if ( !rule->isElse() )
600 const RenderResult res = rule->renderFeature( featToRender, context, renderQueue );
602 matchedAChild |= ( res == Rendered || res == Inactive );
603 rendered |= ( res == Rendered );
608 if ( !matchedAChild )
610 const auto constMElseRules = mElseRules;
611 for (
Rule *rule : constMElseRules )
613 const RenderResult res = rule->renderFeature( featToRender, context, renderQueue );
614 matchedAChild |= ( res == Rendered || res == Inactive );
615 rendered |= res == Rendered;
618 if ( !mIsActive || ( mSymbol && !rendered ) || ( matchedAChild && !rendered ) )
628 if ( !isFilterOK( feature, context ) )
634 const auto constMActiveChildren = mActiveChildren;
635 for (
Rule *rule : constMActiveChildren )
637 if ( rule->isElse() )
639 if ( rule->children().isEmpty() )
641 RuleList lst = rulesForFeature( feature, context,
false );
642 lst.removeOne( rule );
651 return rule->willRenderFeature( feature, context );
654 else if ( rule->willRenderFeature( feature, context ) )
665 if ( !isFilterOK( feature, context ) )
668 lst.append( mSymbol.get() );
670 const auto constMActiveChildren = mActiveChildren;
671 for (
Rule *rule : constMActiveChildren )
673 lst += rule->symbolsForFeature( feature, context );
681 if ( !isFilterOK( feature, context ) )
684 res.insert( mRuleKey );
687 bool matchedNonElseRule =
false;
688 for (
Rule *rule : std::as_const( mActiveChildren ) )
690 if ( rule->isElse() )
694 if ( rule->willRenderFeature( feature, context ) )
696 res.unite( rule->legendKeysForFeature( feature, context ) );
697 matchedNonElseRule =
true;
702 if ( !matchedNonElseRule )
704 for (
Rule *rule : std::as_const( mActiveChildren ) )
706 if ( rule->isElse() )
708 if ( rule->children().isEmpty() )
710 RuleList lst = rulesForFeature( feature, context,
false );
711 lst.removeOne( rule );
715 res.unite( rule->legendKeysForFeature( feature, context ) );
720 res.unite( rule->legendKeysForFeature( feature, context ) );
731 if ( ! isFilterOK( feature, context ) || ( context && ! isScaleOK( context->
rendererScale() ) ) )
739 listChildren = mActiveChildren;
741 const auto constListChildren = listChildren;
742 for (
Rule *rule : constListChildren )
744 lst += rule->rulesForFeature( feature, context, onlyActive );
752 mSymbol->stopRender( context );
754 const auto constMActiveChildren = mActiveChildren;
755 for (
Rule *rule : constMActiveChildren )
757 rule->stopRender( context );
760 mActiveChildren.clear();
761 mSymbolNormZLevels.clear();
766 QString symbolIdx = ruleElem.attribute( QStringLiteral(
"symbol" ) );
768 if ( !symbolIdx.isEmpty() )
770 if ( symbolMap.contains( symbolIdx ) )
772 symbol = symbolMap.take( symbolIdx );
776 QgsDebugError(
"symbol for rule " + symbolIdx +
" not found!" );
780 QString filterExp = ruleElem.attribute( QStringLiteral(
"filter" ) );
781 QString label = ruleElem.attribute( QStringLiteral(
"label" ) );
782 QString description = ruleElem.attribute( QStringLiteral(
"description" ) );
783 int scaleMinDenom = ruleElem.attribute( QStringLiteral(
"scalemindenom" ), QStringLiteral(
"0" ) ).toInt();
784 int scaleMaxDenom = ruleElem.attribute( QStringLiteral(
"scalemaxdenom" ), QStringLiteral(
"0" ) ).toInt();
787 ruleKey = ruleElem.attribute( QStringLiteral(
"key" ) );
789 ruleKey = QUuid::createUuid().toString();
790 Rule *rule =
new Rule( symbol, scaleMinDenom, scaleMaxDenom, filterExp, label, description );
792 if ( !ruleKey.isEmpty() )
793 rule->mRuleKey = ruleKey;
795 rule->
setActive( ruleElem.attribute( QStringLiteral(
"checkstate" ), QStringLiteral(
"1" ) ).toInt() );
797 QDomElement childRuleElem = ruleElem.firstChildElement( QStringLiteral(
"rule" ) );
798 while ( !childRuleElem.isNull() )
800 Rule *childRule =
create( childRuleElem, symbolMap );
807 QgsDebugError( QStringLiteral(
"failed to init a child rule!" ) );
809 childRuleElem = childRuleElem.nextSiblingElement( QStringLiteral(
"rule" ) );
828 if ( ruleElem.localName() != QLatin1String(
"Rule" ) )
830 QgsDebugError( QStringLiteral(
"invalid element: Rule element expected, %1 found!" ).arg( ruleElem.tagName() ) );
834 QString label, description, filterExp;
835 int scaleMinDenom = 0, scaleMaxDenom = 0;
839 QDomElement childElem = ruleElem.firstChildElement();
840 while ( !childElem.isNull() )
842 if ( childElem.localName() == QLatin1String(
"Name" ) )
846 if ( label.isEmpty() )
847 label = childElem.firstChild().nodeValue();
849 else if ( childElem.localName() == QLatin1String(
"Description" ) )
852 QDomElement titleElem = childElem.firstChildElement( QStringLiteral(
"Title" ) );
853 if ( !titleElem.isNull() )
855 label = titleElem.firstChild().nodeValue();
858 QDomElement abstractElem = childElem.firstChildElement( QStringLiteral(
"Abstract" ) );
859 if ( !abstractElem.isNull() )
861 description = abstractElem.firstChild().nodeValue();
864 else if ( childElem.localName() == QLatin1String(
"Abstract" ) )
867 description = childElem.firstChild().nodeValue();
869 else if ( childElem.localName() == QLatin1String(
"Title" ) )
872 label = childElem.firstChild().nodeValue();
874 else if ( childElem.localName() == QLatin1String(
"Filter" ) )
879 if (
filter->hasParserError() )
885 filterExp =
filter->expression();
890 else if ( childElem.localName() == QLatin1String(
"ElseFilter" ) )
892 filterExp = QLatin1String(
"ELSE" );
895 else if ( childElem.localName() == QLatin1String(
"MinScaleDenominator" ) )
898 int v = childElem.firstChild().nodeValue().toInt( &ok );
902 else if ( childElem.localName() == QLatin1String(
"MaxScaleDenominator" ) )
905 int v = childElem.firstChild().nodeValue().toInt( &ok );
909 else if ( childElem.localName().endsWith( QLatin1String(
"Symbolizer" ) ) )
915 childElem = childElem.nextSiblingElement();
920 if ( !layers.isEmpty() )
943 return new Rule( symbol, scaleMinDenom, scaleMaxDenom, filterExp, label, description );
978 bool drawVertexMarker )
998 QList<int> symbolZLevels( symbolZLevelsSet.begin(), symbolZLevelsSet.end() );
999 std::sort( symbolZLevels.begin(), symbolZLevels.end() );
1003 QMap<int, int> zLevelsToNormLevels;
1004 int maxNormLevel = -1;
1005 const auto constSymbolZLevels = symbolZLevels;
1006 for (
int zLevel : constSymbolZLevels )
1008 zLevelsToNormLevels[zLevel] = ++maxNormLevel;
1010 QgsDebugMsgLevel( QStringLiteral(
"zLevel %1 -> %2" ).arg( zLevel ).arg( maxNormLevel ), 4 );
1033 for (
const RenderLevel &level : constMRenderQueue )
1037 for (
const RenderJob *job : std::as_const( level.jobs ) )
1044 for (
int i = 0; i < count; i++ )
1051 int flags = job->ftr.flags;
1093 Q_ASSERT( origDescendants.count() == clonedDescendants.count() );
1094 for (
int i = 0; i < origDescendants.count(); ++i )
1095 clonedDescendants[i]->setRuleKey( origDescendants[i]->ruleKey() );
1117 rendererElem.setAttribute( QStringLiteral(
"type" ), QStringLiteral(
"RuleRenderer" ) );
1122 rulesElem.setTagName( QStringLiteral(
"rules" ) );
1123 rendererElem.appendChild( rulesElem );
1126 rendererElem.appendChild( symbolsElem );
1130 return rendererElem;
1141 return rule ? rule->
active() :
true;
1158 std::function<QString(
Rule *rule )> ruleToExpression;
1159 ruleToExpression = [&ruleToExpression](
Rule * rule ) -> QString
1165 QStringList otherRules;
1166 const QList<QgsRuleBasedRenderer::Rule *> siblings = rule->
parent()->
children();
1167 for (
Rule *sibling : siblings )
1169 if ( sibling == rule || sibling->
isElse() )
1172 const QString siblingExpression = ruleToExpression( sibling );
1173 if ( siblingExpression.isEmpty() )
1174 return QStringLiteral(
"FALSE" );
1176 otherRules.append( siblingExpression );
1179 if ( otherRules.empty() )
1180 return QStringLiteral(
"TRUE" );
1183 otherRules.size() > 1
1184 ? QStringLiteral(
"NOT ((%1))" ).arg( otherRules.join( QLatin1String(
") OR (" ) ) )
1185 : QStringLiteral(
"NOT (%1)" ).arg( otherRules.at( 0 ) )
1190 QStringList ruleParts;
1195 ruleParts.append( QStringLiteral(
"@map_scale <= %1" ).arg( rule->
minimumScale() ) );
1198 ruleParts.append( QStringLiteral(
"@map_scale >= %1" ).arg( rule->
maximumScale() ) );
1200 if ( !ruleParts.empty() )
1203 ruleParts.size() > 1
1204 ? QStringLiteral(
"(%1)" ).arg( ruleParts.join( QLatin1String(
") AND (" ) ) )
1218 const QString ruleFilter = ruleToExpression( rule );
1219 if ( !ruleFilter.isEmpty() )
1220 parts.append( ruleFilter );
1226 return parts.empty() ? QStringLiteral(
"TRUE" )
1227 : ( parts.size() > 1
1228 ? QStringLiteral(
"(%1)" ).arg( parts.join( QLatin1String(
") AND (" ) ) )
1250 QDomElement symbolsElem = element.firstChildElement( QStringLiteral(
"symbols" ) );
1251 if ( symbolsElem.isNull() )
1256 QDomElement rulesElem = element.firstChildElement( QStringLiteral(
"rules" ) );
1273 Rule *root =
nullptr;
1275 QDomElement ruleElem = element.firstChildElement( QStringLiteral(
"Rule" ) );
1276 while ( !ruleElem.isNull() )
1283 root =
new Rule(
nullptr );
1288 ruleElem = ruleElem.nextSiblingElement( QStringLiteral(
"Rule" ) );
1316 const auto constCategories = r->
categories();
1323 else if ( cat.value().type() == QVariant::Int )
1324 value = cat.value().toString();
1325 else if ( cat.value().type() == QVariant::Double )
1328 value = QString::number( cat.value().toDouble(),
'f', 4 );
1331 const QString
filter = QStringLiteral(
"%1 %2 %3" ).arg( attr,
QgsVariantUtils::isNull( cat.value() ) ? QStringLiteral(
"IS" ) : QStringLiteral(
"=" ), value );
1332 const QString label = !cat.label().isEmpty() ? cat.label() :
1333 cat.value().isValid() ? value : QString();
1349 else if ( !testExpr.
isField() )
1352 attr = QStringLiteral(
"(%1)" ).arg( attr );
1355 bool firstRange =
true;
1356 const auto constRanges = r->
ranges();
1361 QString
filter = QStringLiteral(
"%1 %2 %3 AND %1 <= %4" ).arg( attr, firstRange ? QStringLiteral(
">=" ) : QStringLiteral(
">" ),
1362 QString::number( rng.lowerValue(),
'f', 4 ),
1363 QString::number( rng.upperValue(),
'f', 4 ) );
1365 QString label = rng.label().isEmpty() ?
filter : rng.label();
1372 std::sort( scales.begin(), scales.end() );
1376 const auto constScales = scales;
1377 for (
int scale : constScales )
1381 if ( maxDenom != 0 && maxDenom <= scale )
1383 initialRule->
appendChild(
new Rule( symbol->
clone(), oldScale, scale, QString(), QStringLiteral(
"%1 - %2" ).arg( oldScale ).arg( scale ) ) );
1387 initialRule->
appendChild(
new Rule( symbol->
clone(), oldScale, maxDenom, QString(), QStringLiteral(
"%1 - %2" ).arg( oldScale ).arg( maxDenom ) ) );
1392 QString msg( QStringLiteral(
"Rule-based renderer:\n" ) );
1424 std::unique_ptr< QgsRuleBasedRenderer > r;
1425 if ( renderer->
type() == QLatin1String(
"RuleRenderer" ) )
1429 else if ( renderer->
type() == QLatin1String(
"singleSymbol" ) )
1432 if ( !singleSymbolRenderer )
1435 std::unique_ptr< QgsSymbol > origSymbol( singleSymbolRenderer->
symbol()->
clone() );
1436 r = std::make_unique< QgsRuleBasedRenderer >( origSymbol.release() );
1438 else if ( renderer->
type() == QLatin1String(
"categorizedSymbol" ) )
1441 if ( !categorizedRenderer )
1446 bool isField =
false;
1456 if ( isField && !attr.contains(
'\"' ) )
1462 std::unique_ptr< QgsRuleBasedRenderer::Rule > rootrule = std::make_unique< QgsRuleBasedRenderer::Rule >(
nullptr );
1469 std::unique_ptr< QgsRuleBasedRenderer::Rule > rule = std::make_unique< QgsRuleBasedRenderer::Rule >(
nullptr );
1471 rule->setLabel( category.
label() );
1474 if ( category.
value().type() == QVariant::List )
1477 const QVariantList list = category.
value().toList();
1478 for (
const QVariant &v : list )
1481 if ( QVariant( v ).convert( QVariant::Double ) )
1483 values << v.toString();
1491 if ( values.empty() )
1493 expression = QStringLiteral(
"ELSE" );
1497 expression = QStringLiteral(
"%1 IN (%2)" ).arg( attr, values.join(
',' ) );
1503 if ( category.
value().convert( QVariant::Double ) )
1505 value = category.
value().toString();
1513 if ( value == QLatin1String(
"''" ) )
1515 expression = QStringLiteral(
"ELSE" );
1519 expression = QStringLiteral(
"%1 = %2" ).arg( attr, value );
1522 rule->setFilterExpression( expression );
1528 std::unique_ptr< QgsSymbol > origSymbol( category.
symbol()->
clone() );
1529 rule->setSymbol( origSymbol.release() );
1531 rootrule->appendChild( rule.release() );
1534 r = std::make_unique< QgsRuleBasedRenderer >( rootrule.release() );
1536 else if ( renderer->
type() == QLatin1String(
"graduatedSymbol" ) )
1539 if ( !graduatedRenderer )
1545 bool isField =
false;
1555 if ( isField && !attr.contains(
'\"' ) )
1560 else if ( !isField )
1563 attr = QStringLiteral(
"(%1)" ).arg( attr );
1566 std::unique_ptr< QgsRuleBasedRenderer::Rule > rootrule = std::make_unique< QgsRuleBasedRenderer::Rule >(
nullptr );
1570 for (
int i = 0; i < graduatedRenderer->
ranges().size(); ++i )
1572 range = graduatedRenderer->
ranges().value( i );
1573 std::unique_ptr< QgsRuleBasedRenderer::Rule > rule = std::make_unique< QgsRuleBasedRenderer::Rule >(
nullptr );
1574 rule->setLabel( range.
label() );
1577 expression = attr +
" >= " + QString::number( range.
lowerValue(),
'f' ) +
" AND " + \
1578 attr +
" <= " + QString::number( range.
upperValue(),
'f' );
1582 expression = attr +
" > " + QString::number( range.
lowerValue(),
'f' ) +
" AND " + \
1583 attr +
" <= " + QString::number( range.
upperValue(),
'f' );
1585 rule->setFilterExpression( expression );
1591 std::unique_ptr< QgsSymbol > symbol( range.
symbol()->
clone() );
1592 rule->setSymbol( symbol.release() );
1594 rootrule->appendChild( rule.release() );
1597 r = std::make_unique< QgsRuleBasedRenderer >( rootrule.release() );
1599 else if ( renderer->
type() == QLatin1String(
"pointDisplacement" ) || renderer->
type() == QLatin1String(
"pointCluster" ) )
1604 else if ( renderer->
type() == QLatin1String(
"invertedPolygonRenderer" ) )
1609 else if ( renderer->
type() == QLatin1String(
"mergedFeatureRenderer" ) )
1614 else if ( renderer->
type() == QLatin1String(
"embeddedSymbol" ) && layer )
1618 std::unique_ptr< QgsRuleBasedRenderer::Rule > rootrule = std::make_unique< QgsRuleBasedRenderer::Rule >(
nullptr );
1625 while ( it.
nextFeature( feature ) && rootrule->children().size() < 500 )
1629 std::unique_ptr< QgsRuleBasedRenderer::Rule > rule = std::make_unique< QgsRuleBasedRenderer::Rule >(
nullptr );
1630 rule->setFilterExpression( QStringLiteral(
"$id=%1" ).arg( feature.
id() ) );
1631 rule->setLabel( QString::number( feature.
id() ) );
1633 rootrule->appendChild( rule.release() );
1637 std::unique_ptr< QgsRuleBasedRenderer::Rule > rule = std::make_unique< QgsRuleBasedRenderer::Rule >(
nullptr );
1638 rule->setFilterExpression( QStringLiteral(
"ELSE" ) );
1639 rule->setLabel( QObject::tr(
"All other features" ) );
1641 rootrule->appendChild( rule.release() );
1643 r = std::make_unique< QgsRuleBasedRenderer >( rootrule.release() );
1656 QString sizeExpression;
1657 switch ( symbol->
type() )
1663 if ( ! sizeScaleField.isEmpty() )
1665 sizeExpression = QStringLiteral(
"%1*(%2)" ).arg( msl->
size() ).arg( sizeScaleField );
1668 if ( ! rotationField.isEmpty() )
1675 if ( ! sizeScaleField.isEmpty() )
1682 sizeExpression = QStringLiteral(
"%1*(%2)" ).arg( lsl->
width() ).arg( sizeScaleField );
1691 sizeExpression = QStringLiteral(
"%1*(%2)" ).arg( msl->
size() ).arg( sizeScaleField );
GeometryType
The geometry types are used to group Qgis::WkbType in a coarse way.
const QgsCategoryList & categories() const
Returns a list of all categories recognized by the renderer.
QString classAttribute() const
Returns the class attribute for the renderer, which is the field name or expression string from the l...
A vector feature renderer which uses embedded feature symbology to render per-feature symbols.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
Class for parsing and evaluation of expressions (formerly called "search strings").
static QString quotedString(QString text)
Returns a quoted version of a string (in single quotes)
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
bool isField() const
Checks whether an expression consists only of a single field reference.
static QString quotedColumnRef(QString name)
Returns a quoted column reference (in double quotes)
static int expressionToLayerFieldIndex(const QString &expression, const QgsVectorLayer *layer)
Attempts to resolve an expression to a field index from the given layer.
static bool attemptReduceToInClause(const QStringList &expressions, QString &result)
Attempts to reduce a list of expressions to a single "field IN (val1, val2, ... )" type expression.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
virtual void stopRender(QgsRenderContext &context)
Must be called when a render cycle has finished, to allow the renderer to clean up.
void copyRendererData(QgsFeatureRenderer *destRenderer) const
Clones generic renderer data to another renderer.
void saveRendererData(QDomDocument &doc, QDomElement &element, const QgsReadWriteContext &context)
Saves generic renderer data into the specified element.
void renderFeatureWithSymbol(const QgsFeature &feature, QgsSymbol *symbol, QgsRenderContext &context, int layer, bool selected, bool drawVertexMarker)
Render the feature with the symbol using context.
virtual const QgsFeatureRenderer * embeddedRenderer() const
Returns the current embedded renderer (subrenderer) for this feature renderer.
virtual void startRender(QgsRenderContext &context, const QgsFields &fields)
Must be called when a new render cycle is started.
virtual QgsFeatureRenderer * clone() const =0
Create a deep copy of this renderer.
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.
@ EmbeddedSymbols
Retrieve any embedded feature symbology (since QGIS 3.20)
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
QgsFeatureRequest & setNoAttributes()
Set that no attributes will be fetched.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
const QgsSymbol * embeddedSymbol() const
Returns the feature's embedded symbology, or nullptr if the feature has no embedded symbol.
Container of fields for a vector layer.
A fill symbol type, for rendering Polygon and MultiPolygon geometries.
QString classAttribute() const
Returns the attribute name (or expression) used for the classification.
const QgsRangeList & ranges() const
Returns a list of all ranges used in the classification.
QgsInvertedPolygonRenderer is a polygon-only feature renderer used to display features inverted,...
The class stores information about one class/rule of a vector layer renderer in a unified way that ca...
virtual double width() const
Returns the estimated width for the line symbol layer.
A line symbol type, for rendering LineString and MultiLineString geometries.
Abstract base class for marker symbol layers.
double size() const
Returns the symbol size.
A marker symbol type, for rendering Point and MultiPoint geometries.
QgsMergedFeatureRenderer is a polygon or line-only feature renderer used to renderer a set of feature...
static QgsExpression * expressionFromOgcFilter(const QDomElement &element, QgsVectorLayer *layer=nullptr)
Parse XML with OGC filter into QGIS expression.
An abstract base class for distance based point renderers (e.g., clusterer and displacement renderers...
static QgsProperty fromExpression(const QString &expression, bool isActive=true)
Returns a new ExpressionBasedProperty created from the specified expression.
static QgsProperty fromField(const QString &fieldName, bool isActive=true)
Returns a new FieldBasedProperty created from the specified field name.
The class is used as a container of context for various read/write operations on other objects.
Contains information about the context of a rendering operation.
double rendererScale() const
Returns the renderer map scale.
QgsExpressionContext & expressionContext()
Gets the expression context.
bool renderingStopped() const
Returns true if the rendering operation has been stopped and any ongoing rendering should be canceled...
Represents an individual category (class) from a QgsCategorizedSymbolRenderer.
QgsSymbol * symbol() const
Returns the symbol which will be used to render this category.
QVariant value() const
Returns the value corresponding to this category.
QString label() const
Returns the label for this category, which is used to represent the category within legends and the l...
QString label() const
Returns the label used for the range.
QgsSymbol * symbol() const
Returns the symbol used for the range.
double upperValue() const
Returns the upper bound of the range.
double lowerValue() const
Returns the lower bound of the range.
This class keeps data about a rules for rule-based renderer.
bool accept(QgsStyleEntityVisitorInterface *visitor) const
Accepts the specified symbology visitor, causing it to visit all child rules associated with the rule...
QgsRuleBasedRenderer::RuleList descendants() const
Returns all children, grand-children, grand-grand-children, grand-gra... you get it.
void setSymbol(QgsSymbol *sym)
Sets a new symbol (or nullptr). Deletes old symbol.
void removeChild(QgsRuleBasedRenderer::Rule *rule)
delete child rule
QgsRuleBasedRenderer::Rule * findRuleByKey(const QString &key)
Try to find a rule given its unique key.
void insertChild(int i, QgsRuleBasedRenderer::Rule *rule)
add child rule, take ownership, sets this as parent
QString ruleKey() const
Unique rule identifier (for identification of rule within renderer)
bool needsGeometry() const
Returns true if this rule or one of its children needs the geometry to be applied.
QgsRuleBasedRenderer::Rule * takeChild(QgsRuleBasedRenderer::Rule *rule)
take child rule out, set parent as nullptr
const QgsRuleBasedRenderer::RuleList & children() const
Returns all children rules of this rule.
RenderResult
The result of rendering a rule.
@ Rendered
Something was rendered.
QgsRuleBasedRenderer::RuleList rulesForFeature(const QgsFeature &feature, QgsRenderContext *context=nullptr, bool onlyActive=true)
Returns the list of rules used to render the feature in a specific context.
double maximumScale() const
Returns the maximum map scale (i.e.
QgsRuleBasedRenderer::Rule * parent()
The parent rule.
void setIsElse(bool iselse)
Sets if this rule is an ELSE rule.
QgsSymbolList symbolsForFeature(const QgsFeature &feature, QgsRenderContext *context=nullptr)
tell which symbols will be used to render the feature
bool isElse() const
Check if this rule is an ELSE rule.
QSet< QString > legendKeysForFeature(const QgsFeature &feature, QgsRenderContext *context=nullptr)
Returns which legend keys match the feature.
QgsRuleBasedRenderer::Rule * clone() const
clone this rule, return new instance
bool willRenderFeature(const QgsFeature &feature, QgsRenderContext *context=nullptr)
only tell whether a feature will be rendered without actually rendering it
void removeChildAt(int i)
delete child rule
void setActive(bool state)
Sets if this rule is active.
Rule(QgsSymbol *symbol, int maximumScale=0, int minimumScale=0, const QString &filterExp=QString(), const QString &label=QString(), const QString &description=QString(), bool elseRule=false)
Constructor takes ownership of the symbol.
bool isFilterOK(const QgsFeature &f, QgsRenderContext *context=nullptr) const
Check if a given feature shall be rendered by this rule.
QgsSymbolList symbols(const QgsRenderContext &context=QgsRenderContext()) const
bool isScaleOK(double scale) const
Check if this rule applies for a given scale.
static QgsRuleBasedRenderer::Rule * createFromSld(QDomElement &element, Qgis::GeometryType geomType)
Create a rule from the SLD provided in element and for the specified geometry type.
void setNormZLevels(const QMap< int, int > &zLevelsToNormLevels)
assign normalized z-levels [0..N-1] for this rule's symbol for quick access during rendering
QDomElement save(QDomDocument &doc, QgsSymbolMap &symbolMap) const
void appendChild(QgsRuleBasedRenderer::Rule *rule)
add child rule, take ownership, sets this as parent
QgsRuleBasedRenderer::Rule * takeChildAt(int i)
take child rule out, set parent as nullptr
QSet< int > collectZLevels()
Gets all used z-levels from this rule and children.
double minimumScale() const
Returns the minimum map scale (i.e.
void stopRender(QgsRenderContext &context)
Stop a rendering process.
bool hasActiveChildren() const
Returns true if the rule has any active children.
QgsRuleBasedRenderer::Rule::RenderResult renderFeature(QgsRuleBasedRenderer::FeatureToRender &featToRender, QgsRenderContext &context, QgsRuleBasedRenderer::RenderQueue &renderQueue)
Render a given feature, will recursively call subclasses and only render if the constraints apply.
QgsLegendSymbolList legendSymbolItems(int currentLevel=-1) const
QSet< QString > usedAttributes(const QgsRenderContext &context) const
Returns the attributes used to evaluate the expression of this rule.
void setFilterExpression(const QString &filterExp)
Set the expression used to check if a given feature shall be rendered with this rule.
QString dump(int indent=0) const
Dump for debug purpose.
void setRuleKey(const QString &key)
Override the assigned rule key (should be used just internally by rule-based renderer)
bool startRender(QgsRenderContext &context, const QgsFields &fields, QString &filter)
prepare the rule for rendering and its children (build active children array)
static QgsRuleBasedRenderer::Rule * create(QDomElement &ruleElem, QgsSymbolMap &symbolMap, bool reuseId=true)
Create a rule from an XML definition.
QString filterExpression() const
A filter that will check if this rule applies.
bool active() const
Returns if this rule is active.
void toSld(QDomDocument &doc, QDomElement &element, QVariantMap props) const
Saves the symbol layer as SLD.
static void refineRuleCategories(QgsRuleBasedRenderer::Rule *initialRule, QgsCategorizedSymbolRenderer *r)
take a rule and create a list of new rules based on the categories from categorized symbol renderer
static void convertToDataDefinedSymbology(QgsSymbol *symbol, const QString &sizeScaleField, const QString &rotationField=QString())
helper function to convert the size scale and rotation fields present in some other renderers to data...
bool legendSymbolItemChecked(const QString &key) override
Returns true if the legend symbology item with the specified key is checked.
void startRender(QgsRenderContext &context, const QgsFields &fields) override
Must be called when a new render cycle is started.
QDomElement save(QDomDocument &doc, const QgsReadWriteContext &context) override
Stores renderer properties to an XML element.
void setLegendSymbolItem(const QString &key, QgsSymbol *symbol) override
Sets the symbol to be used for a legend symbol item.
bool canSkipRender() override
Returns true if the renderer can be entirely skipped, i.e.
void checkLegendSymbolItem(const QString &key, bool state=true) override
Sets whether the legend symbology item with the specified ley should be checked.
QgsSymbol * symbolForFeature(const QgsFeature &feature, QgsRenderContext &context) const override
Returns symbol for current feature. Should not be used individually: there could be more symbols for ...
QList< QgsRuleBasedRenderer::RenderLevel > RenderQueue
Rendering queue: a list of rendering levels.
QSet< QString > legendKeysForFeature(const QgsFeature &feature, QgsRenderContext &context) const override
Returns legend keys matching a specified feature.
static void refineRuleRanges(QgsRuleBasedRenderer::Rule *initialRule, QgsGraduatedSymbolRenderer *r)
take a rule and create a list of new rules based on the ranges from graduated symbol renderer
QgsSymbolList symbolsForFeature(const QgsFeature &feature, QgsRenderContext &context) const override
Returns list of symbols used for rendering the feature.
QString dump() const override
Returns debug information about this renderer.
QgsSymbolList originalSymbolsForFeature(const QgsFeature &feature, QgsRenderContext &context) const override
Equivalent of originalSymbolsForFeature() call extended to support renderers that may use more symbol...
static QgsRuleBasedRenderer * convertFromRenderer(const QgsFeatureRenderer *renderer, QgsVectorLayer *layer=nullptr)
Creates a new QgsRuleBasedRenderer from an existing renderer.
bool legendSymbolItemsCheckable() const override
Returns true if symbology items in legend are checkable.
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns a list of attributes required by this renderer.
QString filter(const QgsFields &fields=QgsFields()) override
If a renderer does not require all the features this method may be overridden and return an expressio...
bool willRenderFeature(const QgsFeature &feature, QgsRenderContext &context) const override
Returns whether the renderer will render a feature or not.
static void refineRuleScales(QgsRuleBasedRenderer::Rule *initialRule, QList< int > scales)
take a rule and create a list of new rules with intervals of scales given by the passed scale denomin...
QList< QgsRuleBasedRenderer::Rule * > RuleList
void stopRender(QgsRenderContext &context) override
Must be called when a render cycle has finished, to allow the renderer to clean up.
Rule * mRootRule
the root node with hierarchical list of rules
~QgsRuleBasedRenderer() override
bool accept(QgsStyleEntityVisitorInterface *visitor) const override
Accepts the specified symbology visitor, causing it to visit all symbols associated with the renderer...
bool filterNeedsGeometry() const override
Returns true if this renderer requires the geometry to apply the filter.
QgsRuleBasedRenderer * clone() const override
Create a deep copy of this renderer.
static QgsFeatureRenderer * create(QDomElement &element, const QgsReadWriteContext &context)
Creates a new rule-based renderer instance from XML.
QgsLegendSymbolList legendSymbolItems() const override
Returns a list of symbology items for the legend.
static QgsFeatureRenderer * createFromSld(QDomElement &element, Qgis::GeometryType geomType)
Creates a new rule based renderer from an SLD XML element.
QList< FeatureToRender > mCurrentFeatures
QString legendKeyToExpression(const QString &key, QgsVectorLayer *layer, bool &ok) const override
Attempts to convert the specified legend rule key to a QGIS expression matching the features displaye...
bool renderFeature(const QgsFeature &feature, QgsRenderContext &context, int layer=-1, bool selected=false, bool drawVertexMarker=false) override
Render a feature using this renderer in the given context.
QgsRuleBasedRenderer(QgsRuleBasedRenderer::Rule *root)
Constructs the renderer from given tree of rules (takes ownership)
QgsSymbolList symbols(QgsRenderContext &context) const override
Returns list of symbols used by the renderer.
void toSld(QDomDocument &doc, QDomElement &element, const QVariantMap &props=QVariantMap()) const override
used from subclasses to create SLD Rule elements following SLD v1.1 specs
QgsSymbol * symbol() const
Returns the symbol which will be rendered for every feature.
An interface for classes which can visit style entity (e.g.
@ SymbolRule
Rule based symbology or label child rule.
virtual bool visitExit(const QgsStyleEntityVisitorInterface::Node &node)
Called when the visitor stops visiting a node.
virtual bool visitEnter(const QgsStyleEntityVisitorInterface::Node &node)
Called when the visitor starts visiting a node.
virtual bool visit(const QgsStyleEntityVisitorInterface::StyleLeaf &entity)
Called when the visitor will visit a style entity.
A symbol entity for QgsStyle databases.
static void applyScaleDependency(QDomDocument &doc, QDomElement &ruleElem, QVariantMap &props)
Checks if the properties contain scaleMinDenom and scaleMaxDenom, if available, they are added into t...
static bool createFunctionElement(QDomDocument &doc, QDomElement &element, const QString &function)
static bool createSymbolLayerListFromSld(QDomElement &element, Qgis::GeometryType geomType, QList< QgsSymbolLayer * > &layers)
Creates a symbol layer list from a DOM element.
static void mergeScaleDependencies(double mScaleMinDenom, double mScaleMaxDenom, QVariantMap &props)
Merges the local scale limits, if any, with the ones already in the map, if any.
static void clearSymbolMap(QgsSymbolMap &symbols)
static QgsSymbolMap loadSymbols(QDomElement &element, const QgsReadWriteContext &context)
Reads a collection of symbols from XML and returns them in a map. Caller is responsible for deleting ...
static QDomElement saveSymbols(QgsSymbolMap &symbols, const QString &tagName, QDomDocument &doc, const QgsReadWriteContext &context)
Writes a collection of symbols to XML with specified tagName for the top-level element.
@ PropertyAngle
Symbol angle.
@ PropertySize
Symbol size.
@ PropertyStrokeWidth
Stroke width.
virtual QString layerType() const =0
Returns a string that represents this layer type.
int renderingPass() const
Specifies the rendering pass in which this symbol layer should be rendered.
virtual void setDataDefinedProperty(Property key, const QgsProperty &property)
Sets a data defined property for the layer.
virtual QgsSymbol * subSymbol()
Returns the symbol's sub symbol, if present.
Abstract base class for all rendered symbols.
QgsSymbolLayer * symbolLayer(int layer)
Returns the symbol layer at the specified index.
virtual QgsSymbol * clone() const =0
Returns a deep copy of this symbol.
int symbolLayerCount() const
Returns the total number of symbol layers contained in the symbol.
Qgis::SymbolType type() const
Returns the symbol's type.
static bool isNull(const QVariant &variant)
Returns true if the specified variant should be considered a NULL value.
Represents a vector layer which manages a vector based data sets.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
QList< QgsLegendSymbolItem > QgsLegendSymbolList
#define QgsDebugMsgLevel(str, level)
#define QgsDebugError(str)
#define RENDERER_TAG_NAME
QMap< QString, QgsSymbol * > QgsSymbolMap
QList< QgsSymbol * > QgsSymbolList
QList< QgsSymbolLayer * > QgsSymbolLayerList
Feature for rendering by a QgsRuleBasedRenderer.
A QgsRuleBasedRenderer rendering job, consisting of a feature to be rendered with a particular symbol...
Render level: a list of jobs to be drawn at particular level for a QgsRuleBasedRenderer.
Contains information relating to a node (i.e.
Contains information relating to the style entity currently being visited.