38#include <QDomDocument>
46 , mMaximumScale( scaleMinDenom )
47 , mMinimumScale( scaleMaxDenom )
48 , mFilterExp( filterExp )
50 , mDescription( description )
51 , mElseRule( elseRule )
54 mFilterExp = QStringLiteral(
"ELSE" );
56 mRuleKey = QUuid::createUuid().toString();
62 qDeleteAll( mChildren );
68 if ( mFilterExp.trimmed().compare( QLatin1String(
"ELSE" ), Qt::CaseInsensitive ) == 0 )
73 else if ( mFilterExp.trimmed().isEmpty() )
81 mFilter = std::make_unique< QgsExpression >( mFilterExp );
87 mChildren.append( rule );
94 mChildren.insert( i, rule );
101 mChildren.removeAll( rule );
108 delete mChildren.takeAt( i );
114 mChildren.removeAll( rule );
115 rule->mParent =
nullptr;
122 Rule *rule = mChildren.takeAt( i );
123 rule->mParent =
nullptr;
132 if ( key == mRuleKey )
135 const auto constMChildren = mChildren;
136 for (
Rule *rule : constMChildren )
145void QgsRuleBasedRenderer::Rule::updateElseRules()
148 const auto constMChildren = mChildren;
149 for (
Rule *rule : constMChildren )
151 if ( rule->isElse() )
158 mFilterExp = QStringLiteral(
"ELSE" );
176 if ( !mChildren.empty() )
178 for (
const Rule *rule : mChildren )
181 if ( !rule->accept( visitor ) )
195 off.fill( QChar(
' ' ), indent );
196 QString symbolDump = ( mSymbol ? mSymbol->dump() : QStringLiteral(
"[]" ) );
197 QString msg = off + QStringLiteral(
"RULE %1 - scale [%2,%3] - filter %4 - symbol %5\n" )
198 .arg( mLabel ).arg( mMaximumScale ).arg( mMinimumScale )
199 .arg( mFilterExp, symbolDump );
202 const auto constMChildren = mChildren;
203 for (
Rule *rule : constMChildren )
205 lst.append( rule->dump( indent + 2 ) );
207 msg += lst.join( QLatin1Char(
'\n' ) );
216 attrs.unite(
mFilter->referencedColumns() );
218 attrs.unite( mSymbol->usedAttributes( context ) );
221 const auto constMChildren = mChildren;
222 for (
Rule *rule : constMChildren )
224 attrs.unite( rule->usedAttributes( context ) );
234 const auto constMChildren = mChildren;
235 for (
Rule *rule : constMChildren )
237 if ( rule->needsGeometry() )
248 lst.append( mSymbol.get() );
250 const auto constMChildren = mChildren;
251 for (
Rule *rule : constMChildren )
253 lst += rule->symbols( context );
260 mSymbol.reset( sym );
265 mFilterExp = filterExp;
272 if ( currentLevel != -1 )
274 lst <<
QgsLegendSymbolItem( mSymbol.get(), mLabel, mRuleKey,
true, mMaximumScale, mMinimumScale, currentLevel, mParent ? mParent->mRuleKey : QString() );
277 for ( RuleList::const_iterator it = mChildren.constBegin(); it != mChildren.constEnd(); ++it )
288 if ( !
mFilter || mElseRule || ! context )
302 if ( !
qgsDoubleNear( mMaximumScale, 0.0 ) && mMaximumScale > scale )
304 if ( !
qgsDoubleNear( mMinimumScale, 0.0 ) && mMinimumScale < scale )
312 Rule *newrule =
new Rule( sym, mMaximumScale, mMinimumScale, mFilterExp, mLabel, mDescription );
315 const auto constMChildren = mChildren;
316 for (
Rule *rule : constMChildren )
323 QDomElement ruleElem = doc.createElement( QStringLiteral(
"rule" ) );
327 int symbolIndex = symbolMap.size();
328 symbolMap[QString::number( symbolIndex )] = mSymbol.get();
329 ruleElem.setAttribute( QStringLiteral(
"symbol" ), symbolIndex );
331 if ( !mFilterExp.isEmpty() )
332 ruleElem.setAttribute( QStringLiteral(
"filter" ), mFilterExp );
333 if ( mMaximumScale != 0 )
334 ruleElem.setAttribute( QStringLiteral(
"scalemindenom" ), mMaximumScale );
335 if ( mMinimumScale != 0 )
336 ruleElem.setAttribute( QStringLiteral(
"scalemaxdenom" ), mMinimumScale );
337 if ( !mLabel.isEmpty() )
338 ruleElem.setAttribute( QStringLiteral(
"label" ), mLabel );
339 if ( !mDescription.isEmpty() )
340 ruleElem.setAttribute( QStringLiteral(
"description" ), mDescription );
342 ruleElem.setAttribute( QStringLiteral(
"checkstate" ), 0 );
343 ruleElem.setAttribute( QStringLiteral(
"key" ), mRuleKey );
345 const auto constMChildren = mChildren;
346 for (
Rule *rule : constMChildren )
348 ruleElem.appendChild( rule->save( doc, symbolMap ) );
357 if (
symbols( context ).isEmpty() )
360 if ( !mFilterExp.isEmpty() )
362 QString
filter = props.value( QStringLiteral(
"filter" ), QString() ).toString();
364 filter += QLatin1String(
" AND " );
366 props[ QStringLiteral(
"filter" )] =
filter;
373 QDomElement ruleElem = doc.createElement( QStringLiteral(
"se:Rule" ) );
374 element.appendChild( ruleElem );
378 QDomElement nameElem = doc.createElement( QStringLiteral(
"se:Name" ) );
379 nameElem.appendChild( doc.createTextNode( mLabel ) );
380 ruleElem.appendChild( nameElem );
382 if ( !mLabel.isEmpty() || !mDescription.isEmpty() )
384 QDomElement descrElem = doc.createElement( QStringLiteral(
"se:Description" ) );
385 if ( !mLabel.isEmpty() )
387 QDomElement titleElem = doc.createElement( QStringLiteral(
"se:Title" ) );
388 titleElem.appendChild( doc.createTextNode( mLabel ) );
389 descrElem.appendChild( titleElem );
391 if ( !mDescription.isEmpty() )
393 QDomElement abstractElem = doc.createElement( QStringLiteral(
"se:Abstract" ) );
394 abstractElem.appendChild( doc.createTextNode( mDescription ) );
395 descrElem.appendChild( abstractElem );
397 ruleElem.appendChild( descrElem );
400 if ( !props.value( QStringLiteral(
"filter" ), QString() ).toString().isEmpty() )
407 mSymbol->toSld( doc, ruleElem, props );
411 const auto constMChildren = mChildren;
412 for (
Rule *rule : constMChildren )
414 rule->toSld( doc, element, props );
420 mActiveChildren.clear();
433 mSymbol->startRender( context, fields );
437 QStringList subfilters;
438 const auto constMChildren = mChildren;
439 for (
Rule *rule : constMChildren )
442 if ( rule->startRender( context, fields, subfilter ) )
445 mActiveChildren.append( rule );
446 subfilters.append( subfilter );
454 if ( subfilters.length() > 1 || !subfilters.value( 0 ).isEmpty() )
456 if ( subfilters.contains( QStringLiteral(
"TRUE" ) ) )
458 sf = QStringLiteral(
"TRUE" );
469 else if ( subfilters.count() > 50 )
471 std::function<QString(
const QStringList & )>bt = [ &bt ](
const QStringList & subf )
473 if ( subf.count( ) == 1 )
477 else if ( subf.count( ) == 2 )
479 return subf.join( QLatin1String(
") OR (" ) ).prepend(
'(' ).append(
')' );
483 int midpos =
static_cast<int>( subf.length() / 2 );
484 return QStringLiteral(
"(%1) OR (%2)" ).arg( bt( subf.mid( 0, midpos ) ), bt( subf.mid( midpos ) ) );
487 sf = bt( subfilters );
491 sf = subfilters.join( QLatin1String(
") OR (" ) ).prepend(
'(' ).append(
')' );
503 if ( mSymbol || sf.isEmpty() )
504 filter = QStringLiteral(
"TRUE" );
510 else if ( !mFilterExp.trimmed().isEmpty() && !sf.isEmpty() )
511 filter = QStringLiteral(
"(%1) AND (%2)" ).arg( mFilterExp, sf );
512 else if ( !mFilterExp.trimmed().isEmpty() )
514 else if ( sf.isEmpty() )
515 filter = QStringLiteral(
"TRUE" );
526 return !mActiveChildren.empty();
531 QSet<int> symbolZLevelsSet;
537 for (
int i = 0; i < mSymbol->symbolLayerCount(); i++ )
539 symbolZLevelsSet.insert( mSymbol->symbolLayer( i )->renderingPass() );
544 QList<Rule *>::iterator it;
545 for ( it = mActiveChildren.begin(); it != mActiveChildren.end(); ++it )
550 return symbolZLevelsSet;
557 for (
int i = 0; i < mSymbol->symbolLayerCount(); i++ )
559 int normLevel = zLevelsToNormLevels.value( mSymbol->symbolLayer( i )->renderingPass() );
560 mSymbolNormZLevels.insert( normLevel );
565 const auto constMActiveChildren = mActiveChildren;
566 for (
Rule *rule : constMActiveChildren )
568 rule->setNormZLevels( zLevelsToNormLevels );
575 if ( !isFilterOK( featToRender.
feat, &context ) )
578 bool rendered =
false;
581 if ( mSymbol && mIsActive )
584 const auto constMSymbolNormZLevels = mSymbolNormZLevels;
585 for (
int normZLevel : constMSymbolNormZLevels )
588 renderQueue[normZLevel].jobs.append(
new RenderJob( featToRender, mSymbol.get() ) );
593 bool matchedAChild =
false;
596 const auto constMChildren = mChildren;
597 for (
Rule *rule : constMChildren )
600 if ( !rule->isElse() )
602 const RenderResult res = rule->renderFeature( featToRender, context, renderQueue );
604 matchedAChild |= ( res == Rendered || res == Inactive );
605 rendered |= ( res == Rendered );
610 if ( !matchedAChild )
612 const auto constMElseRules = mElseRules;
613 for (
Rule *rule : constMElseRules )
615 const RenderResult res = rule->renderFeature( featToRender, context, renderQueue );
616 matchedAChild |= ( res == Rendered || res == Inactive );
617 rendered |= res == Rendered;
620 if ( !mIsActive || ( mSymbol && !rendered ) || ( matchedAChild && !rendered ) )
630 if ( !isFilterOK( feature, context ) )
636 const auto constMActiveChildren = mActiveChildren;
637 for (
Rule *rule : constMActiveChildren )
639 if ( rule->isElse() )
641 if ( rule->children().isEmpty() )
643 RuleList lst = rulesForFeature( feature, context,
false );
644 lst.removeOne( rule );
653 return rule->willRenderFeature( feature, context );
656 else if ( rule->willRenderFeature( feature, context ) )
667 if ( !isFilterOK( feature, context ) )
670 lst.append( mSymbol.get() );
672 const auto constMActiveChildren = mActiveChildren;
673 for (
Rule *rule : constMActiveChildren )
675 lst += rule->symbolsForFeature( feature, context );
683 if ( !isFilterOK( feature, context ) )
685 lst.insert( mRuleKey );
687 const auto constMActiveChildren = mActiveChildren;
688 for (
Rule *rule : constMActiveChildren )
690 bool validKey =
false;
691 if ( rule->isElse() )
693 if ( rule->children().isEmpty() )
695 RuleList lst = rulesForFeature( feature, context,
false );
696 lst.removeOne( rule );
708 else if ( rule->willRenderFeature( feature, context ) )
715 lst.unite( rule->legendKeysForFeature( feature, context ) );
724 if ( ! isFilterOK( feature, context ) || ( context && ! isScaleOK( context->
rendererScale() ) ) )
732 listChildren = mActiveChildren;
734 const auto constListChildren = listChildren;
735 for (
Rule *rule : constListChildren )
737 lst += rule->rulesForFeature( feature, context, onlyActive );
745 mSymbol->stopRender( context );
747 const auto constMActiveChildren = mActiveChildren;
748 for (
Rule *rule : constMActiveChildren )
750 rule->stopRender( context );
753 mActiveChildren.clear();
754 mSymbolNormZLevels.clear();
759 QString symbolIdx = ruleElem.attribute( QStringLiteral(
"symbol" ) );
761 if ( !symbolIdx.isEmpty() )
763 if ( symbolMap.contains( symbolIdx ) )
765 symbol = symbolMap.take( symbolIdx );
769 QgsDebugMsg(
"symbol for rule " + symbolIdx +
" not found!" );
773 QString filterExp = ruleElem.attribute( QStringLiteral(
"filter" ) );
774 QString label = ruleElem.attribute( QStringLiteral(
"label" ) );
775 QString description = ruleElem.attribute( QStringLiteral(
"description" ) );
776 int scaleMinDenom = ruleElem.attribute( QStringLiteral(
"scalemindenom" ), QStringLiteral(
"0" ) ).toInt();
777 int scaleMaxDenom = ruleElem.attribute( QStringLiteral(
"scalemaxdenom" ), QStringLiteral(
"0" ) ).toInt();
780 ruleKey = ruleElem.attribute( QStringLiteral(
"key" ) );
782 ruleKey = QUuid::createUuid().toString();
783 Rule *rule =
new Rule( symbol, scaleMinDenom, scaleMaxDenom, filterExp, label, description );
785 if ( !ruleKey.isEmpty() )
786 rule->mRuleKey = ruleKey;
788 rule->
setActive( ruleElem.attribute( QStringLiteral(
"checkstate" ), QStringLiteral(
"1" ) ).toInt() );
790 QDomElement childRuleElem = ruleElem.firstChildElement( QStringLiteral(
"rule" ) );
791 while ( !childRuleElem.isNull() )
793 Rule *childRule =
create( childRuleElem, symbolMap );
800 QgsDebugMsg( QStringLiteral(
"failed to init a child rule!" ) );
802 childRuleElem = childRuleElem.nextSiblingElement( QStringLiteral(
"rule" ) );
814 l +=
c->descendants();
821 if ( ruleElem.localName() != QLatin1String(
"Rule" ) )
823 QgsDebugMsg( QStringLiteral(
"invalid element: Rule element expected, %1 found!" ).arg( ruleElem.tagName() ) );
827 QString label, description, filterExp;
828 int scaleMinDenom = 0, scaleMaxDenom = 0;
832 QDomElement childElem = ruleElem.firstChildElement();
833 while ( !childElem.isNull() )
835 if ( childElem.localName() == QLatin1String(
"Name" ) )
839 if ( label.isEmpty() )
840 label = childElem.firstChild().nodeValue();
842 else if ( childElem.localName() == QLatin1String(
"Description" ) )
845 QDomElement titleElem = childElem.firstChildElement( QStringLiteral(
"Title" ) );
846 if ( !titleElem.isNull() )
848 label = titleElem.firstChild().nodeValue();
851 QDomElement abstractElem = childElem.firstChildElement( QStringLiteral(
"Abstract" ) );
852 if ( !abstractElem.isNull() )
854 description = abstractElem.firstChild().nodeValue();
857 else if ( childElem.localName() == QLatin1String(
"Abstract" ) )
860 description = childElem.firstChild().nodeValue();
862 else if ( childElem.localName() == QLatin1String(
"Title" ) )
865 label = childElem.firstChild().nodeValue();
867 else if ( childElem.localName() == QLatin1String(
"Filter" ) )
872 if (
filter->hasParserError() )
878 filterExp =
filter->expression();
883 else if ( childElem.localName() == QLatin1String(
"ElseFilter" ) )
885 filterExp = QLatin1String(
"ELSE" );
888 else if ( childElem.localName() == QLatin1String(
"MinScaleDenominator" ) )
891 int v = childElem.firstChild().nodeValue().toInt( &ok );
895 else if ( childElem.localName() == QLatin1String(
"MaxScaleDenominator" ) )
898 int v = childElem.firstChild().nodeValue().toInt( &ok );
902 else if ( childElem.localName().endsWith( QLatin1String(
"Symbolizer" ) ) )
908 childElem = childElem.nextSiblingElement();
913 if ( !layers.isEmpty() )
917 case Qgis::GeometryType::Line:
921 case Qgis::GeometryType::Polygon:
925 case Qgis::GeometryType::Point:
936 return new Rule( symbol, scaleMinDenom, scaleMaxDenom, filterExp, label, description );
971 bool drawVertexMarker )
991 QList<int> symbolZLevels( symbolZLevelsSet.begin(), symbolZLevelsSet.end() );
992 std::sort( symbolZLevels.begin(), symbolZLevels.end() );
996 QMap<int, int> zLevelsToNormLevels;
997 int maxNormLevel = -1;
998 const auto constSymbolZLevels = symbolZLevels;
999 for (
int zLevel : constSymbolZLevels )
1001 zLevelsToNormLevels[zLevel] = ++maxNormLevel;
1003 QgsDebugMsgLevel( QStringLiteral(
"zLevel %1 -> %2" ).arg( zLevel ).arg( maxNormLevel ), 4 );
1026 for (
const RenderLevel &level : constMRenderQueue )
1030 for (
const RenderJob *job : std::as_const( level.jobs ) )
1037 for (
int i = 0; i < count; i++ )
1044 int flags = job->ftr.flags;
1086 Q_ASSERT( origDescendants.count() == clonedDescendants.count() );
1087 for (
int i = 0; i < origDescendants.count(); ++i )
1088 clonedDescendants[i]->setRuleKey( origDescendants[i]->ruleKey() );
1110 rendererElem.setAttribute( QStringLiteral(
"type" ), QStringLiteral(
"RuleRenderer" ) );
1115 rulesElem.setTagName( QStringLiteral(
"rules" ) );
1116 rendererElem.appendChild( rulesElem );
1119 rendererElem.appendChild( symbolsElem );
1123 return rendererElem;
1134 return rule ? rule->
active() :
true;
1151 std::function<QString(
Rule *rule )> ruleToExpression;
1152 ruleToExpression = [&ruleToExpression](
Rule * rule ) -> QString
1158 QStringList otherRules;
1159 const QList<QgsRuleBasedRenderer::Rule *> siblings = rule->
parent()->
children();
1160 for (
Rule *sibling : siblings )
1162 if ( sibling == rule || sibling->
isElse() )
1165 const QString siblingExpression = ruleToExpression( sibling );
1166 if ( siblingExpression.isEmpty() )
1167 return QStringLiteral(
"FALSE" );
1169 otherRules.append( siblingExpression );
1172 if ( otherRules.empty() )
1173 return QStringLiteral(
"TRUE" );
1176 otherRules.size() > 1
1177 ? QStringLiteral(
"NOT ((%1))" ).arg( otherRules.join( QLatin1String(
") OR (" ) ) )
1178 : QStringLiteral(
"NOT (%1)" ).arg( otherRules.at( 0 ) )
1183 QStringList ruleParts;
1188 ruleParts.append( QStringLiteral(
"@map_scale <= %1" ).arg( rule->
minimumScale() ) );
1191 ruleParts.append( QStringLiteral(
"@map_scale >= %1" ).arg( rule->
maximumScale() ) );
1193 if ( !ruleParts.empty() )
1196 ruleParts.size() > 1
1197 ? QStringLiteral(
"(%1)" ).arg( ruleParts.join( QLatin1String(
") AND (" ) ) )
1211 const QString ruleFilter = ruleToExpression( rule );
1212 if ( !ruleFilter.isEmpty() )
1213 parts.append( ruleFilter );
1219 return parts.empty() ? QStringLiteral(
"TRUE" )
1220 : ( parts.size() > 1
1221 ? QStringLiteral(
"(%1)" ).arg( parts.join( QLatin1String(
") AND (" ) ) )
1243 QDomElement symbolsElem = element.firstChildElement( QStringLiteral(
"symbols" ) );
1244 if ( symbolsElem.isNull() )
1249 QDomElement rulesElem = element.firstChildElement( QStringLiteral(
"rules" ) );
1266 Rule *root =
nullptr;
1268 QDomElement ruleElem = element.firstChildElement( QStringLiteral(
"Rule" ) );
1269 while ( !ruleElem.isNull() )
1276 root =
new Rule(
nullptr );
1281 ruleElem = ruleElem.nextSiblingElement( QStringLiteral(
"Rule" ) );
1309 const auto constCategories = r->
categories();
1316 else if ( cat.value().type() == QVariant::Int )
1317 value = cat.value().toString();
1318 else if ( cat.value().type() == QVariant::Double )
1321 value = QString::number( cat.value().toDouble(),
'f', 4 );
1324 const QString
filter = QStringLiteral(
"%1 %2 %3" ).arg( attr,
QgsVariantUtils::isNull( cat.value() ) ? QStringLiteral(
"IS" ) : QStringLiteral(
"=" ), value );
1325 const QString label = !cat.label().isEmpty() ? cat.label() :
1326 cat.value().isValid() ? value : QString();
1342 else if ( !testExpr.
isField() )
1345 attr = QStringLiteral(
"(%1)" ).arg( attr );
1348 bool firstRange =
true;
1349 const auto constRanges = r->
ranges();
1354 QString
filter = QStringLiteral(
"%1 %2 %3 AND %1 <= %4" ).arg( attr, firstRange ? QStringLiteral(
">=" ) : QStringLiteral(
">" ),
1355 QString::number( rng.lowerValue(),
'f', 4 ),
1356 QString::number( rng.upperValue(),
'f', 4 ) );
1358 QString label = rng.label().isEmpty() ?
filter : rng.label();
1365 std::sort( scales.begin(), scales.end() );
1369 const auto constScales = scales;
1370 for (
int scale : constScales )
1374 if ( maxDenom != 0 && maxDenom <= scale )
1376 initialRule->
appendChild(
new Rule( symbol->
clone(), oldScale, scale, QString(), QStringLiteral(
"%1 - %2" ).arg( oldScale ).arg( scale ) ) );
1380 initialRule->
appendChild(
new Rule( symbol->
clone(), oldScale, maxDenom, QString(), QStringLiteral(
"%1 - %2" ).arg( oldScale ).arg( maxDenom ) ) );
1385 QString msg( QStringLiteral(
"Rule-based renderer:\n" ) );
1417 std::unique_ptr< QgsRuleBasedRenderer > r;
1418 if ( renderer->
type() == QLatin1String(
"RuleRenderer" ) )
1422 else if ( renderer->
type() == QLatin1String(
"singleSymbol" ) )
1425 if ( !singleSymbolRenderer )
1428 std::unique_ptr< QgsSymbol > origSymbol( singleSymbolRenderer->
symbol()->
clone() );
1429 r = std::make_unique< QgsRuleBasedRenderer >( origSymbol.release() );
1431 else if ( renderer->
type() == QLatin1String(
"categorizedSymbol" ) )
1434 if ( !categorizedRenderer )
1439 bool isField =
false;
1449 if ( isField && !attr.contains(
'\"' ) )
1455 std::unique_ptr< QgsRuleBasedRenderer::Rule > rootrule = std::make_unique< QgsRuleBasedRenderer::Rule >(
nullptr );
1462 std::unique_ptr< QgsRuleBasedRenderer::Rule > rule = std::make_unique< QgsRuleBasedRenderer::Rule >(
nullptr );
1464 rule->setLabel( category.
label() );
1467 if ( category.
value().type() == QVariant::List )
1470 const QVariantList list = category.
value().toList();
1471 for (
const QVariant &v : list )
1474 if ( QVariant( v ).convert( QVariant::Double ) )
1476 values << v.toString();
1484 if ( values.empty() )
1486 expression = QStringLiteral(
"ELSE" );
1490 expression = QStringLiteral(
"%1 IN (%2)" ).arg( attr, values.join(
',' ) );
1496 if ( category.
value().convert( QVariant::Double ) )
1498 value = category.
value().toString();
1506 if ( value == QLatin1String(
"''" ) )
1508 expression = QStringLiteral(
"ELSE" );
1512 expression = QStringLiteral(
"%1 = %2" ).arg( attr, value );
1515 rule->setFilterExpression( expression );
1521 std::unique_ptr< QgsSymbol > origSymbol( category.
symbol()->
clone() );
1522 rule->setSymbol( origSymbol.release() );
1524 rootrule->appendChild( rule.release() );
1527 r = std::make_unique< QgsRuleBasedRenderer >( rootrule.release() );
1529 else if ( renderer->
type() == QLatin1String(
"graduatedSymbol" ) )
1532 if ( !graduatedRenderer )
1538 bool isField =
false;
1548 if ( isField && !attr.contains(
'\"' ) )
1553 else if ( !isField )
1556 attr = QStringLiteral(
"(%1)" ).arg( attr );
1559 std::unique_ptr< QgsRuleBasedRenderer::Rule > rootrule = std::make_unique< QgsRuleBasedRenderer::Rule >(
nullptr );
1563 for (
int i = 0; i < graduatedRenderer->
ranges().size(); ++i )
1565 range = graduatedRenderer->
ranges().value( i );
1566 std::unique_ptr< QgsRuleBasedRenderer::Rule > rule = std::make_unique< QgsRuleBasedRenderer::Rule >(
nullptr );
1567 rule->setLabel( range.
label() );
1570 expression = attr +
" >= " + QString::number( range.
lowerValue(),
'f' ) +
" AND " + \
1571 attr +
" <= " + QString::number( range.
upperValue(),
'f' );
1575 expression = attr +
" > " + QString::number( range.
lowerValue(),
'f' ) +
" AND " + \
1576 attr +
" <= " + QString::number( range.
upperValue(),
'f' );
1578 rule->setFilterExpression( expression );
1584 std::unique_ptr< QgsSymbol > symbol( range.
symbol()->
clone() );
1585 rule->setSymbol( symbol.release() );
1587 rootrule->appendChild( rule.release() );
1590 r = std::make_unique< QgsRuleBasedRenderer >( rootrule.release() );
1592 else if ( renderer->
type() == QLatin1String(
"pointDisplacement" ) || renderer->
type() == QLatin1String(
"pointCluster" ) )
1597 else if ( renderer->
type() == QLatin1String(
"invertedPolygonRenderer" ) )
1602 else if ( renderer->
type() == QLatin1String(
"mergedFeatureRenderer" ) )
1607 else if ( renderer->
type() == QLatin1String(
"embeddedSymbol" ) && layer )
1611 std::unique_ptr< QgsRuleBasedRenderer::Rule > rootrule = std::make_unique< QgsRuleBasedRenderer::Rule >(
nullptr );
1618 while ( it.
nextFeature( feature ) && rootrule->children().size() < 500 )
1622 std::unique_ptr< QgsRuleBasedRenderer::Rule > rule = std::make_unique< QgsRuleBasedRenderer::Rule >(
nullptr );
1623 rule->setFilterExpression( QStringLiteral(
"$id=%1" ).arg( feature.
id() ) );
1624 rule->setLabel( QString::number( feature.
id() ) );
1626 rootrule->appendChild( rule.release() );
1630 std::unique_ptr< QgsRuleBasedRenderer::Rule > rule = std::make_unique< QgsRuleBasedRenderer::Rule >(
nullptr );
1631 rule->setFilterExpression( QStringLiteral(
"ELSE" ) );
1632 rule->setLabel( QObject::tr(
"All other features" ) );
1634 rootrule->appendChild( rule.release() );
1636 r = std::make_unique< QgsRuleBasedRenderer >( rootrule.release() );
1649 QString sizeExpression;
1650 switch ( symbol->
type() )
1656 if ( ! sizeScaleField.isEmpty() )
1658 sizeExpression = QStringLiteral(
"%1*(%2)" ).arg( msl->
size() ).arg( sizeScaleField );
1661 if ( ! rotationField.isEmpty() )
1668 if ( ! sizeScaleField.isEmpty() )
1675 sizeExpression = QStringLiteral(
"%1*(%2)" ).arg( lsl->
width() ).arg( sizeScaleField );
1684 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)
void renderFeatureWithSymbol(const QgsFeature &feature, QgsSymbol *symbol, QgsRenderContext &context, int layer, bool selected, bool drawVertexMarker) SIP_THROW(QgsCsException)
Render the feature with the symbol using context.
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.
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
items of symbology items in legend 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
item in symbology was 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
items of symbology items in legend should be 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.
bool renderFeature(const QgsFeature &feature, QgsRenderContext &context, int layer=-1, bool selected=false, bool drawVertexMarker=false) override SIP_THROW(QgsCsException)
Render a feature using this renderer in the given context.
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...
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 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.