QGIS API Documentation  3.2.0-Bonn (bc43194)
qgscategorizedsymbolrenderer.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgscategorizedsymbolrenderer.cpp
3  ---------------------
4  begin : November 2009
5  copyright : (C) 2009 by Martin Dobias
6  email : wonder dot sk at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 #include <algorithm>
16 
18 
20 #include "qgssymbol.h"
21 #include "qgssymbollayerutils.h"
22 #include "qgscolorramp.h"
25 #include "qgspainteffect.h"
26 #include "qgspainteffectregistry.h"
27 #include "qgssymbollayer.h"
28 #include "qgsfeature.h"
29 #include "qgsvectorlayer.h"
30 #include "qgslogger.h"
31 #include "qgsproperty.h"
32 
33 #include <QDomDocument>
34 #include <QDomElement>
35 #include <QSettings> // for legend
36 
37 QgsRendererCategory::QgsRendererCategory( const QVariant &value, QgsSymbol *symbol, const QString &label, bool render )
38  : mValue( value )
39  , mSymbol( symbol )
40  , mLabel( label )
41  , mRender( render )
42 {
43 }
44 
46  : mValue( cat.mValue )
47  , mSymbol( cat.mSymbol ? cat.mSymbol->clone() : nullptr )
48  , mLabel( cat.mLabel )
49  , mRender( cat.mRender )
50 {
51 }
52 
53 // copy+swap idion, the copy is done through the 'pass by value'
55 {
56  swap( cat );
57  return *this;
58 }
59 
61 {
62  std::swap( mValue, cat.mValue );
63  std::swap( mSymbol, cat.mSymbol );
64  std::swap( mLabel, cat.mLabel );
65 }
66 
68 {
69  return mValue;
70 }
71 
73 {
74  return mSymbol.get();
75 }
76 
78 {
79  return mLabel;
80 }
81 
83 {
84  return mRender;
85 }
86 
87 void QgsRendererCategory::setValue( const QVariant &value )
88 {
89  mValue = value;
90 }
91 
93 {
94  if ( mSymbol.get() != s ) mSymbol.reset( s );
95 }
96 
97 void QgsRendererCategory::setLabel( const QString &label )
98 {
99  mLabel = label;
100 }
101 
103 {
104  mRender = render;
105 }
106 
108 {
109  return QStringLiteral( "%1::%2::%3:%4\n" ).arg( mValue.toString(), mLabel, mSymbol->dump() ).arg( mRender );
110 }
111 
112 void QgsRendererCategory::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
113 {
114  if ( !mSymbol.get() || props.value( QStringLiteral( "attribute" ), QLatin1String( "" ) ).isEmpty() )
115  return;
116 
117  QString attrName = props[ QStringLiteral( "attribute" )];
118 
119  QDomElement ruleElem = doc.createElement( QStringLiteral( "se:Rule" ) );
120  element.appendChild( ruleElem );
121 
122  QDomElement nameElem = doc.createElement( QStringLiteral( "se:Name" ) );
123  nameElem.appendChild( doc.createTextNode( mLabel ) );
124  ruleElem.appendChild( nameElem );
125 
126  QDomElement descrElem = doc.createElement( QStringLiteral( "se:Description" ) );
127  QDomElement titleElem = doc.createElement( QStringLiteral( "se:Title" ) );
128  QString descrStr = QStringLiteral( "%1 is '%2'" ).arg( attrName, mValue.toString() );
129  titleElem.appendChild( doc.createTextNode( !mLabel.isEmpty() ? mLabel : descrStr ) );
130  descrElem.appendChild( titleElem );
131  ruleElem.appendChild( descrElem );
132 
133  // create the ogc:Filter for the range
134  QString filterFunc;
135  if ( mValue.isNull() || mValue.toString().isEmpty() )
136  {
137  filterFunc = QStringLiteral( "%1 = '%2' or %1 is null" )
138  .arg( attrName.replace( '\"', QLatin1String( "\"\"" ) ),
139  mValue.toString().replace( '\'', QLatin1String( "''" ) ) );
140  }
141  else
142  {
143  filterFunc = QStringLiteral( "%1 = '%2'" )
144  .arg( attrName.replace( '\"', QLatin1String( "\"\"" ) ),
145  mValue.toString().replace( '\'', QLatin1String( "''" ) ) );
146  }
147 
148  QgsSymbolLayerUtils::createFunctionElement( doc, ruleElem, filterFunc );
149 
150  // add the mix/max scale denoms if we got any from the callers
151  QgsSymbolLayerUtils::applyScaleDependency( doc, ruleElem, props );
152 
153  mSymbol->toSld( doc, ruleElem, props );
154 }
155 
157 
159  : QgsFeatureRenderer( QStringLiteral( "categorizedSymbol" ) )
160  , mAttrName( attrName )
161 {
162  //important - we need a deep copy of the categories list, not a shared copy. This is required because
163  //QgsRendererCategory::symbol() is marked const, and so retrieving the symbol via this method does not
164  //trigger a detachment and copy of mCategories BUT that same method CAN be used to modify a symbol in place
165  Q_FOREACH ( const QgsRendererCategory &cat, categories )
166  {
167  if ( !cat.symbol() )
168  {
169  QgsDebugMsg( "invalid symbol in a category! ignoring..." );
170  }
171  mCategories << cat;
172  }
173 }
174 
176 {
177  mSymbolHash.clear();
178 
179  for ( int i = 0; i < mCategories.size(); ++i )
180  {
181  const QgsRendererCategory &cat = mCategories.at( i );
182  mSymbolHash.insert( cat.value().toString(), ( cat.renderState() || mCounting ) ? cat.symbol() : nullptr );
183  }
184 }
185 
187 {
188  return nullptr;
189 }
190 
192 {
193  bool found = false;
194  return symbolForValue( value, found );
195 }
196 
197 QgsSymbol *QgsCategorizedSymbolRenderer::symbolForValue( const QVariant &value, bool &foundMatchingSymbol ) const
198 {
199  foundMatchingSymbol = false;
200 
201  // TODO: special case for int, double
202  QHash<QString, QgsSymbol *>::const_iterator it = mSymbolHash.constFind( value.isNull() ? QLatin1String( "" ) : value.toString() );
203  if ( it == mSymbolHash.constEnd() )
204  {
205  if ( mSymbolHash.isEmpty() )
206  {
207  QgsDebugMsg( "there are no hashed symbols!!!" );
208  }
209  else
210  {
211  QgsDebugMsgLevel( "attribute value not found: " + value.toString(), 3 );
212  }
213  return nullptr;
214  }
215 
216  foundMatchingSymbol = true;
217 
218  return *it;
219 }
220 
222 {
223  return originalSymbolForFeature( feature, context );
224 }
225 
226 QVariant QgsCategorizedSymbolRenderer::valueForFeature( const QgsFeature &feature, QgsRenderContext &context ) const
227 {
228  QgsAttributes attrs = feature.attributes();
229  QVariant value;
230  if ( mAttrNum == -1 )
231  {
232  Q_ASSERT( mExpression );
233 
234  value = mExpression->evaluate( &context.expressionContext() );
235  }
236  else
237  {
238  value = attrs.value( mAttrNum );
239  }
240 
241  return value;
242 }
243 
245 {
246  QVariant value = valueForFeature( feature, context );
247 
248  bool foundCategory = false;
249  // find the right symbol for the category
250  QgsSymbol *symbol = symbolForValue( value, foundCategory );
251 
252  if ( !foundCategory )
253  {
254  // if no symbol found, use default symbol
255  return symbolForValue( QVariant( "" ), foundCategory );
256  }
257 
258  return symbol;
259 }
260 
261 
263 {
264  for ( int i = 0; i < mCategories.count(); i++ )
265  {
266  if ( mCategories[i].value() == val )
267  return i;
268  }
269  return -1;
270 }
271 
273 {
274  int idx = -1;
275  for ( int i = 0; i < mCategories.count(); i++ )
276  {
277  if ( mCategories[i].label() == val )
278  {
279  if ( idx != -1 )
280  return -1;
281  else
282  idx = i;
283  }
284  }
285  return idx;
286 }
287 
288 bool QgsCategorizedSymbolRenderer::updateCategoryValue( int catIndex, const QVariant &value )
289 {
290  if ( catIndex < 0 || catIndex >= mCategories.size() )
291  return false;
292  mCategories[catIndex].setValue( value );
293  return true;
294 }
295 
297 {
298  if ( catIndex < 0 || catIndex >= mCategories.size() )
299  return false;
300  mCategories[catIndex].setSymbol( symbol );
301  return true;
302 }
303 
304 bool QgsCategorizedSymbolRenderer::updateCategoryLabel( int catIndex, const QString &label )
305 {
306  if ( catIndex < 0 || catIndex >= mCategories.size() )
307  return false;
308  mCategories[catIndex].setLabel( label );
309  return true;
310 }
311 
313 {
314  if ( catIndex < 0 || catIndex >= mCategories.size() )
315  return false;
316  mCategories[catIndex].setRenderState( render );
317  return true;
318 }
319 
321 {
322  if ( !cat.symbol() )
323  {
324  QgsDebugMsg( "invalid symbol in a category! ignoring..." );
325  return;
326  }
327 
328  mCategories.append( cat );
329 }
330 
332 {
333  if ( catIndex < 0 || catIndex >= mCategories.size() )
334  return false;
335 
336  mCategories.removeAt( catIndex );
337  return true;
338 }
339 
341 {
342  mCategories.clear();
343 }
344 
346 {
347  if ( from < 0 || from >= mCategories.size() || to < 0 || to >= mCategories.size() ) return;
348  mCategories.move( from, to );
349 }
350 
352 {
353  return qgsVariantLessThan( c1.value(), c2.value() );
354 }
356 {
357  return qgsVariantGreaterThan( c1.value(), c2.value() );
358 }
359 
360 void QgsCategorizedSymbolRenderer::sortByValue( Qt::SortOrder order )
361 {
362  if ( order == Qt::AscendingOrder )
363  {
364  std::sort( mCategories.begin(), mCategories.end(), valueLessThan );
365  }
366  else
367  {
368  std::sort( mCategories.begin(), mCategories.end(), valueGreaterThan );
369  }
370 }
371 
373 {
374  return QString::localeAwareCompare( c1.label(), c2.label() ) < 0;
375 }
376 
378 {
379  return !labelLessThan( c1, c2 );
380 }
381 
382 void QgsCategorizedSymbolRenderer::sortByLabel( Qt::SortOrder order )
383 {
384  if ( order == Qt::AscendingOrder )
385  {
386  std::sort( mCategories.begin(), mCategories.end(), labelLessThan );
387  }
388  else
389  {
390  std::sort( mCategories.begin(), mCategories.end(), labelGreaterThan );
391  }
392 }
393 
395 {
396  QgsFeatureRenderer::startRender( context, fields );
397 
398  mCounting = context.rendererScale() == 0.0;
399 
400  // make sure that the hash table is up to date
401  rebuildHash();
402 
403  // find out classification attribute index from name
404  mAttrNum = fields.lookupField( mAttrName );
405  if ( mAttrNum == -1 )
406  {
407  mExpression.reset( new QgsExpression( mAttrName ) );
408  mExpression->prepare( &context.expressionContext() );
409  }
410 
411  Q_FOREACH ( const QgsRendererCategory &cat, mCategories )
412  {
413  cat.symbol()->startRender( context, fields );
414  }
415 }
416 
418 {
420 
421  Q_FOREACH ( const QgsRendererCategory &cat, mCategories )
422  {
423  cat.symbol()->stopRender( context );
424  }
425  mExpression.reset();
426 }
427 
429 {
430  QSet<QString> attributes;
431 
432  // mAttrName can contain either attribute name or an expression.
433  // Sometimes it is not possible to distinguish between those two,
434  // e.g. "a - b" can be both a valid attribute name or expression.
435  // Since we do not have access to fields here, try both options.
436  attributes << mAttrName;
437 
438  QgsExpression testExpr( mAttrName );
439  if ( !testExpr.hasParserError() )
440  attributes.unite( testExpr.referencedColumns() );
441 
442  QgsCategoryList::const_iterator catIt = mCategories.constBegin();
443  for ( ; catIt != mCategories.constEnd(); ++catIt )
444  {
445  QgsSymbol *catSymbol = catIt->symbol();
446  if ( catSymbol )
447  {
448  attributes.unite( catSymbol->usedAttributes( context ) );
449  }
450  }
451  return attributes;
452 }
453 
455 {
456  QString s = QStringLiteral( "CATEGORIZED: idx %1\n" ).arg( mAttrName );
457  for ( int i = 0; i < mCategories.count(); i++ )
458  s += mCategories[i].dump();
459  return s;
460 }
461 
463 {
465  if ( mSourceSymbol )
466  r->setSourceSymbol( mSourceSymbol->clone() );
467  if ( mSourceColorRamp )
468  {
469  r->setSourceColorRamp( mSourceColorRamp->clone() );
470  }
473 
474  copyRendererData( r );
475  return r;
476 }
477 
478 void QgsCategorizedSymbolRenderer::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
479 {
480  QgsStringMap newProps = props;
481  newProps[ QStringLiteral( "attribute" )] = mAttrName;
482 
483  // create a Rule for each range
484  for ( QgsCategoryList::const_iterator it = mCategories.constBegin(); it != mCategories.constEnd(); ++it )
485  {
486  it->toSld( doc, element, newProps );
487  }
488 }
489 
491 {
492  int attrNum = fields.lookupField( mAttrName );
493  bool isExpression = ( attrNum == -1 );
494 
495  bool hasDefault = false;
496  bool defaultActive = false;
497  bool allActive = true;
498  bool noneActive = true;
499 
500  //we need to build lists of both inactive and active values, as either list may be required
501  //depending on whether the default category is active or not
502  QString activeValues;
503  QString inactiveValues;
504 
505  Q_FOREACH ( const QgsRendererCategory &cat, mCategories )
506  {
507  if ( cat.value() == "" )
508  {
509  hasDefault = true;
510  defaultActive = cat.renderState();
511  }
512 
513  noneActive = noneActive && !cat.renderState();
514  allActive = allActive && cat.renderState();
515 
516  QVariant::Type valType = isExpression ? cat.value().type() : fields.at( attrNum ).type();
517  QString value = QgsExpression::quotedValue( cat.value(), valType );
518 
519  if ( !cat.renderState() )
520  {
521  if ( cat.value() != "" )
522  {
523  if ( !inactiveValues.isEmpty() )
524  inactiveValues.append( ',' );
525 
526  inactiveValues.append( value );
527  }
528  }
529  else
530  {
531  if ( cat.value() != "" )
532  {
533  if ( !activeValues.isEmpty() )
534  activeValues.append( ',' );
535 
536  activeValues.append( value );
537  }
538  }
539  }
540 
541  QString attr = isExpression ? mAttrName : QStringLiteral( "\"%1\"" ).arg( mAttrName );
542 
543  if ( allActive && hasDefault )
544  {
545  return QString();
546  }
547  else if ( noneActive )
548  {
549  return QStringLiteral( "FALSE" );
550  }
551  else if ( defaultActive )
552  {
553  return QStringLiteral( "(%1) NOT IN (%2) OR (%1) IS NULL" ).arg( attr, inactiveValues );
554  }
555  else
556  {
557  return QStringLiteral( "(%1) IN (%2)" ).arg( attr, activeValues );
558  }
559 }
560 
562 {
563  Q_UNUSED( context );
564  QgsSymbolList lst;
565  lst.reserve( mCategories.count() );
566  Q_FOREACH ( const QgsRendererCategory &cat, mCategories )
567  {
568  lst.append( cat.symbol() );
569  }
570  return lst;
571 }
572 
574 {
575  QDomElement symbolsElem = element.firstChildElement( QStringLiteral( "symbols" ) );
576  if ( symbolsElem.isNull() )
577  return nullptr;
578 
579  QDomElement catsElem = element.firstChildElement( QStringLiteral( "categories" ) );
580  if ( catsElem.isNull() )
581  return nullptr;
582 
583  QgsSymbolMap symbolMap = QgsSymbolLayerUtils::loadSymbols( symbolsElem, context );
584  QgsCategoryList cats;
585 
586  QDomElement catElem = catsElem.firstChildElement();
587  while ( !catElem.isNull() )
588  {
589  if ( catElem.tagName() == QLatin1String( "category" ) )
590  {
591  QVariant value = QVariant( catElem.attribute( QStringLiteral( "value" ) ) );
592  QString symbolName = catElem.attribute( QStringLiteral( "symbol" ) );
593  QString label = catElem.attribute( QStringLiteral( "label" ) );
594  bool render = catElem.attribute( QStringLiteral( "render" ) ) != QLatin1String( "false" );
595  if ( symbolMap.contains( symbolName ) )
596  {
597  QgsSymbol *symbol = symbolMap.take( symbolName );
598  cats.append( QgsRendererCategory( value, symbol, label, render ) );
599  }
600  }
601  catElem = catElem.nextSiblingElement();
602  }
603 
604  QString attrName = element.attribute( QStringLiteral( "attr" ) );
605 
607 
608  // delete symbols if there are any more
610 
611  // try to load source symbol (optional)
612  QDomElement sourceSymbolElem = element.firstChildElement( QStringLiteral( "source-symbol" ) );
613  if ( !sourceSymbolElem.isNull() )
614  {
615  QgsSymbolMap sourceSymbolMap = QgsSymbolLayerUtils::loadSymbols( sourceSymbolElem, context );
616  if ( sourceSymbolMap.contains( QStringLiteral( "0" ) ) )
617  {
618  r->setSourceSymbol( sourceSymbolMap.take( QStringLiteral( "0" ) ) );
619  }
620  QgsSymbolLayerUtils::clearSymbolMap( sourceSymbolMap );
621  }
622 
623  // try to load color ramp (optional)
624  QDomElement sourceColorRampElem = element.firstChildElement( QStringLiteral( "colorramp" ) );
625  if ( !sourceColorRampElem.isNull() && sourceColorRampElem.attribute( QStringLiteral( "name" ) ) == QLatin1String( "[source]" ) )
626  {
627  r->setSourceColorRamp( QgsSymbolLayerUtils::loadColorRamp( sourceColorRampElem ) );
628  }
629 
630  QDomElement rotationElem = element.firstChildElement( QStringLiteral( "rotation" ) );
631  if ( !rotationElem.isNull() && !rotationElem.attribute( QStringLiteral( "field" ) ).isEmpty() )
632  {
633  Q_FOREACH ( const QgsRendererCategory &cat, r->mCategories )
634  {
635  convertSymbolRotation( cat.symbol(), rotationElem.attribute( QStringLiteral( "field" ) ) );
636  }
637  if ( r->mSourceSymbol )
638  {
639  convertSymbolRotation( r->mSourceSymbol.get(), rotationElem.attribute( QStringLiteral( "field" ) ) );
640  }
641  }
642 
643  QDomElement sizeScaleElem = element.firstChildElement( QStringLiteral( "sizescale" ) );
644  if ( !sizeScaleElem.isNull() && !sizeScaleElem.attribute( QStringLiteral( "field" ) ).isEmpty() )
645  {
646  Q_FOREACH ( const QgsRendererCategory &cat, r->mCategories )
647  {
649  QgsSymbolLayerUtils::decodeScaleMethod( sizeScaleElem.attribute( QStringLiteral( "scalemethod" ) ) ),
650  sizeScaleElem.attribute( QStringLiteral( "field" ) ) );
651  }
652  if ( r->mSourceSymbol && r->mSourceSymbol->type() == QgsSymbol::Marker )
653  {
655  QgsSymbolLayerUtils::decodeScaleMethod( sizeScaleElem.attribute( QStringLiteral( "scalemethod" ) ) ),
656  sizeScaleElem.attribute( QStringLiteral( "field" ) ) );
657  }
658  }
659 
660  QDomElement ddsLegendSizeElem = element.firstChildElement( QStringLiteral( "data-defined-size-legend" ) );
661  if ( !ddsLegendSizeElem.isNull() )
662  {
663  r->mDataDefinedSizeLegend.reset( QgsDataDefinedSizeLegend::readXml( ddsLegendSizeElem, context ) );
664  }
665 
666  // TODO: symbol levels
667  return r;
668 }
669 
670 QDomElement QgsCategorizedSymbolRenderer::save( QDomDocument &doc, const QgsReadWriteContext &context )
671 {
672  // clazy:skip
673  QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
674  rendererElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "categorizedSymbol" ) );
675  rendererElem.setAttribute( QStringLiteral( "symbollevels" ), ( mUsingSymbolLevels ? QStringLiteral( "1" ) : QStringLiteral( "0" ) ) );
676  rendererElem.setAttribute( QStringLiteral( "forceraster" ), ( mForceRaster ? QStringLiteral( "1" ) : QStringLiteral( "0" ) ) );
677  rendererElem.setAttribute( QStringLiteral( "attr" ), mAttrName );
678 
679  // categories
680  if ( !mCategories.isEmpty() )
681  {
682  int i = 0;
684  QDomElement catsElem = doc.createElement( QStringLiteral( "categories" ) );
685  QgsCategoryList::const_iterator it = mCategories.constBegin();
686  for ( ; it != mCategories.constEnd(); ++it )
687  {
688  const QgsRendererCategory &cat = *it;
689  QString symbolName = QString::number( i );
690  symbols.insert( symbolName, cat.symbol() );
691 
692  QDomElement catElem = doc.createElement( QStringLiteral( "category" ) );
693  catElem.setAttribute( QStringLiteral( "value" ), cat.value().toString() );
694  catElem.setAttribute( QStringLiteral( "symbol" ), symbolName );
695  catElem.setAttribute( QStringLiteral( "label" ), cat.label() );
696  catElem.setAttribute( QStringLiteral( "render" ), cat.renderState() ? "true" : "false" );
697  catsElem.appendChild( catElem );
698  i++;
699  }
700  rendererElem.appendChild( catsElem );
701 
702  // save symbols
703  QDomElement symbolsElem = QgsSymbolLayerUtils::saveSymbols( symbols, QStringLiteral( "symbols" ), doc, context );
704  rendererElem.appendChild( symbolsElem );
705 
706  }
707 
708  // save source symbol
709  if ( mSourceSymbol )
710  {
711  QgsSymbolMap sourceSymbols;
712  sourceSymbols.insert( QStringLiteral( "0" ), mSourceSymbol.get() );
713  QDomElement sourceSymbolElem = QgsSymbolLayerUtils::saveSymbols( sourceSymbols, QStringLiteral( "source-symbol" ), doc, context );
714  rendererElem.appendChild( sourceSymbolElem );
715  }
716 
717  // save source color ramp
718  if ( mSourceColorRamp )
719  {
720  QDomElement colorRampElem = QgsSymbolLayerUtils::saveColorRamp( QStringLiteral( "[source]" ), mSourceColorRamp.get(), doc );
721  rendererElem.appendChild( colorRampElem );
722  }
723 
724  QDomElement rotationElem = doc.createElement( QStringLiteral( "rotation" ) );
725  rendererElem.appendChild( rotationElem );
726 
727  QDomElement sizeScaleElem = doc.createElement( QStringLiteral( "sizescale" ) );
728  rendererElem.appendChild( sizeScaleElem );
729 
731  mPaintEffect->saveProperties( doc, rendererElem );
732 
733  if ( !mOrderBy.isEmpty() )
734  {
735  QDomElement orderBy = doc.createElement( QStringLiteral( "orderby" ) );
736  mOrderBy.save( orderBy );
737  rendererElem.appendChild( orderBy );
738  }
739  rendererElem.setAttribute( QStringLiteral( "enableorderby" ), ( mOrderByEnabled ? QStringLiteral( "1" ) : QStringLiteral( "0" ) ) );
740 
742  {
743  QDomElement ddsLegendElem = doc.createElement( QStringLiteral( "data-defined-size-legend" ) );
744  mDataDefinedSizeLegend->writeXml( ddsLegendElem, context );
745  rendererElem.appendChild( ddsLegendElem );
746  }
747 
748  return rendererElem;
749 }
750 
751 
752 QgsLegendSymbolList QgsCategorizedSymbolRenderer::baseLegendSymbolItems() const
753 {
755  int i = 0;
756  Q_FOREACH ( const QgsRendererCategory &cat, mCategories )
757  {
758  lst << QgsLegendSymbolItem( cat.symbol(), cat.label(), QString::number( i++ ), true );
759  }
760  return lst;
761 }
762 
764 {
766  {
767  // check that all symbols that have the same size expression
768  QgsProperty ddSize;
769  Q_FOREACH ( const QgsRendererCategory &category, mCategories )
770  {
771  const QgsMarkerSymbol *symbol = static_cast<const QgsMarkerSymbol *>( category.symbol() );
772  if ( ddSize )
773  {
774  QgsProperty sSize( symbol->dataDefinedSize() );
775  if ( sSize != ddSize )
776  {
777  // no common size expression
778  return baseLegendSymbolItems();
779  }
780  }
781  else
782  {
783  ddSize = symbol->dataDefinedSize();
784  }
785  }
786 
787  if ( ddSize && ddSize.isActive() )
788  {
790 
792  ddSizeLegend.updateFromSymbolAndProperty( static_cast<const QgsMarkerSymbol *>( mSourceSymbol.get() ), ddSize );
793  lst += ddSizeLegend.legendSymbolList();
794 
795  lst += baseLegendSymbolItems();
796  return lst;
797  }
798  }
799 
800  return baseLegendSymbolItems();
801 }
802 
804 {
805  QString value = valueForFeature( feature, context ).toString();
806  int i = 0;
807 
808  Q_FOREACH ( const QgsRendererCategory &cat, mCategories )
809  {
810  if ( value == cat.value() )
811  {
812  if ( cat.renderState() || mCounting )
813  return QSet< QString >() << QString::number( i );
814  else
815  return QSet< QString >();
816  }
817  i++;
818  }
819 
820  return QSet< QString >();
821 }
822 
824 {
825  return mSourceSymbol.get();
826 }
828 {
829  mSourceSymbol.reset( sym );
830 }
831 
833 {
834  return mSourceColorRamp.get();
835 }
836 
838 {
839  mSourceColorRamp.reset( ramp );
840 }
841 
843 {
844  setSourceColorRamp( ramp );
845  double num = mCategories.count() - 1;
846  double count = 0;
847 
848  QgsRandomColorRamp *randomRamp = dynamic_cast<QgsRandomColorRamp *>( ramp );
849  if ( randomRamp )
850  {
851  //ramp is a random colors ramp, so inform it of the total number of required colors
852  //this allows the ramp to pregenerate a set of visually distinctive colors
853  randomRamp->setTotalColorCount( mCategories.count() );
854  }
855 
856  Q_FOREACH ( const QgsRendererCategory &cat, mCategories )
857  {
858  double value = count / num;
859  cat.symbol()->setColor( mSourceColorRamp->color( value ) );
860  count += 1;
861  }
862 }
863 
865 {
866  int i = 0;
867  Q_FOREACH ( const QgsRendererCategory &cat, mCategories )
868  {
869  QgsSymbol *symbol = sym->clone();
870  symbol->setColor( cat.symbol()->color() );
871  updateCategorySymbol( i, symbol );
872  ++i;
873  }
874  setSourceSymbol( sym->clone() );
875 }
876 
878 {
879  return true;
880 }
881 
883 {
884  bool ok;
885  int index = key.toInt( &ok );
886  if ( ok && index >= 0 && index < mCategories.size() )
887  return mCategories.at( index ).renderState();
888  else
889  return true;
890 }
891 
893 {
894  bool ok;
895  int index = key.toInt( &ok );
896  if ( ok )
897  updateCategorySymbol( index, symbol );
898  else
899  delete symbol;
900 }
901 
902 void QgsCategorizedSymbolRenderer::checkLegendSymbolItem( const QString &key, bool state )
903 {
904  bool ok;
905  int index = key.toInt( &ok );
906  if ( ok )
907  updateCategoryRenderState( index, state );
908 }
909 
911 {
912  QgsCategorizedSymbolRenderer *r = nullptr;
913  if ( renderer->type() == QLatin1String( "categorizedSymbol" ) )
914  {
915  r = dynamic_cast<QgsCategorizedSymbolRenderer *>( renderer->clone() );
916  }
917  else if ( renderer->type() == QLatin1String( "pointDisplacement" ) || renderer->type() == QLatin1String( "pointCluster" ) )
918  {
919  const QgsPointDistanceRenderer *pointDistanceRenderer = dynamic_cast<const QgsPointDistanceRenderer *>( renderer );
920  if ( pointDistanceRenderer )
921  r = convertFromRenderer( pointDistanceRenderer->embeddedRenderer() );
922  }
923  else if ( renderer->type() == QLatin1String( "invertedPolygonRenderer" ) )
924  {
925  const QgsInvertedPolygonRenderer *invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer *>( renderer );
926  if ( invertedPolygonRenderer )
927  r = convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() );
928  }
929 
930  // If not one of the specifically handled renderers, then just grab the symbol from the renderer
931  // Could have applied this to specific renderer types (singleSymbol, graduatedSymbol)
932 
933  if ( !r )
934  {
935  r = new QgsCategorizedSymbolRenderer( QLatin1String( "" ), QgsCategoryList() );
936  QgsRenderContext context;
937  QgsSymbolList symbols = const_cast<QgsFeatureRenderer *>( renderer )->symbols( context );
938  if ( !symbols.isEmpty() )
939  {
940  r->setSourceSymbol( symbols.at( 0 )->clone() );
941  }
942  }
943 
944  r->setOrderBy( renderer->orderBy() );
945  r->setOrderByEnabled( renderer->orderByEnabled() );
946 
947  return r;
948 }
949 
951 {
952  mDataDefinedSizeLegend.reset( settings );
953 }
954 
956 {
957  return mDataDefinedSizeLegend.get();
958 }
An abstract base class for distance based point renderers (e.g., clusterer and displacement renderers...
int lookupField(const QString &fieldName) const
Look up field&#39;s index from the field name.
Definition: qgsfields.cpp:299
Q_DECL_DEPRECATED QgsSymbol * skipRender()
Class for parsing and evaluation of expressions (formerly called "search strings").
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
The class is used as a container of context for various read/write operations on other objects...
double rendererScale() const
Returns the renderer map scale.
QgsSymbolList symbols(QgsRenderContext &context) const override
Returns list of symbols used by the renderer.
static QgsSymbol::ScaleMethod decodeScaleMethod(const QString &str)
QList< QgsLegendSymbolItem > QgsLegendSymbolList
QgsFeatureRequest::OrderBy mOrderBy
Definition: qgsrenderer.h:526
void updateColorRamp(QgsColorRamp *ramp)
Update the color ramp used and all symbols colors.
static bool createFunctionElement(QDomDocument &doc, QDomElement &element, const QString &function)
bool updateCategoryRenderState(int catIndex, bool render)
static void applyScaleDependency(QDomDocument &doc, QDomElement &ruleElem, QgsStringMap &props)
Checks if the properties contain scaleMinDenom and scaleMaxDenom, if available, they are added into t...
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
static QgsFeatureRenderer * create(QDomElement &element, const QgsReadWriteContext &context)
create renderer from XML element
QgsFeatureRequest::OrderBy orderBy() const
Gets the order in which features shall be processed by this renderer.
QgsCategorizedSymbolRenderer(const QString &attrName=QString(), const QgsCategoryList &categories=QgsCategoryList())
Abstract base class for color ramps.
Definition: qgscolorramp.h:31
void moveCategory(int from, int to)
Moves the category at index position from to index position to.
static bool isDefaultStack(QgsPaintEffect *effect)
Tests whether a paint effect matches the default effects stack.
static QDomElement saveColorRamp(const QString &name, QgsColorRamp *ramp, QDomDocument &doc)
Encodes a color ramp&#39;s settings to an XML element.
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...
Container of fields for a vector layer.
Definition: qgsfields.h:42
#define RENDERER_TAG_NAME
Definition: qgsrenderer.h:49
void setUsingSymbolLevels(bool usingSymbolLevels)
Definition: qgsrenderer.h:272
void checkLegendSymbolItem(const QString &key, bool state=true) override
item in symbology was checked
bool updateCategoryLabel(int catIndex, const QString &label)
static void clearSymbolMap(QgsSymbolMap &symbols)
QgsLegendSymbolList legendSymbolItems() const override
Returns a list of symbology items for the legend.
QgsPaintEffect * mPaintEffect
Definition: qgsrenderer.h:510
QSet< QString > referencedColumns() const
Gets list of columns referenced by the expression.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:62
std::unique_ptr< QgsSymbol > mSourceSymbol
int categoryIndexForLabel(const QString &val)
Returns index of category with specified label (-1 if not found or not unique)
QgsLegendSymbolList legendSymbolList() const
Generates legend symbol items according to the configuration.
bool qgsVariantGreaterThan(const QVariant &lhs, const QVariant &rhs)
Compares two QVariant values and returns whether the first is greater than the second.
Definition: qgis.cpp:214
void swap(QgsRendererCategory &other)
QMap< QString, QString > QgsStringMap
Definition: qgis.h:501
bool qgsVariantLessThan(const QVariant &lhs, const QVariant &rhs)
Compares two QVariant values and returns whether the first is less than the second.
Definition: qgis.cpp:146
bool valueGreaterThan(const QgsRendererCategory &c1, const QgsRendererCategory &c2)
void startRender(QgsRenderContext &context, const QgsFields &fields=QgsFields())
Begins the rendering process for the symbol.
Definition: qgssymbol.cpp:396
QgsField at(int i) const
Gets field at particular index (must be in range 0..N-1)
Definition: qgsfields.cpp:145
QList< QgsRendererCategory > QgsCategoryList
QgsCategorizedSymbolRenderer * clone() const override
Create a deep copy of this renderer.
QDomElement save(QDomDocument &doc, const QgsReadWriteContext &context) override
store renderer info to XML element
QgsDataDefinedSizeLegend * dataDefinedSizeLegend() const
Returns configuration of appearance of legend when using data-defined size for marker symbols...
QList< QgsSymbol * > QgsSymbolList
Definition: qgsrenderer.h:43
QString type() const
Definition: qgsrenderer.h:129
void toSld(QDomDocument &doc, QDomElement &element, QgsStringMap props) const
QgsInvertedPolygonRenderer is a polygon-only feature renderer used to display features inverted...
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
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.
bool labelGreaterThan(const QgsRendererCategory &c1, const QgsRendererCategory &c2)
std::unique_ptr< QgsSymbol > mSymbol
QgsSymbol * originalSymbolForFeature(const QgsFeature &feature, QgsRenderContext &context) const override
Returns symbol for feature.
void sortByLabel(Qt::SortOrder order=Qt::AscendingOrder)
void startRender(QgsRenderContext &context, const QgsFields &fields) override
Must be called when a new render cycle is started.
void updateFromSymbolAndProperty(const QgsMarkerSymbol *symbol, const QgsProperty &ddSize)
Updates the list of classes, source symbol and title label from given symbol and property.
static QgsColorRamp * loadColorRamp(QDomElement &element)
Creates a color ramp from the settings encoded in an XML element.
QColor color() const
Definition: qgssymbol.cpp:449
static void convertSymbolSizeScale(QgsSymbol *symbol, QgsSymbol::ScaleMethod method, const QString &field)
bool legendSymbolItemChecked(const QString &key) override
items of symbology items in legend is checked
void setLegendSymbolItem(const QString &key, QgsSymbol *symbol) override
Sets the symbol to be used for a legend symbol item.
QgsSymbol * sourceSymbol()
Returns the renderer&#39;s source symbol, which is the base symbol used for the each categories&#39; symbol b...
A store for object properties.
Definition: qgsproperty.h:229
virtual void setTotalColorCount(int colorCount)
Sets the desired total number of unique colors for the resultant ramp.
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 ...
void stopRender(QgsRenderContext &context) override
Must be called when a render cycle has finished, to allow the renderer to clean up.
bool orderByEnabled() const
Returns whether custom ordering will be applied before features are processed by this renderer...
bool renderState() const
Returns true if the category is currently enabled and should be rendered.
bool labelLessThan(const QgsRendererCategory &c1, const QgsRendererCategory &c2)
void setRenderState(bool render)
Sets whether the category is currently enabled and should be rendered.
void setOrderBy(const QgsFeatureRequest::OrderBy &orderBy)
Define the order in which features shall be processed by this renderer.
Totally random color ramp.
Definition: qgscolorramp.h:427
Q_DECL_DEPRECATED QgsSymbol * symbolForValue(const QVariant &value) const
Returns the matching symbol corresponding to an attribute value.
QString dump() const override
Returns debug information about this renderer.
QgsExpressionContext & expressionContext()
Gets the expression context.
QgsProperty dataDefinedSize() const
Returns data defined size for whole symbol (including all symbol layers).
Definition: qgssymbol.cpp:1374
void setDataDefinedSizeLegend(QgsDataDefinedSizeLegend *settings)
Configures appearance of legend when renderer is configured to use data-defined size for marker symbo...
bool legendSymbolItemsCheckable() const override
items of symbology items in legend should be checkable
static void convertSymbolRotation(QgsSymbol *symbol, const QString &field)
Marker symbol.
Definition: qgssymbol.h:85
const QgsFeatureRenderer * embeddedRenderer() const override
Returns the current embedded renderer (subrenderer) for this feature renderer.
QSet< QString > usedAttributes(const QgsRenderContext &context) const
Returns a list of attributes required to render this feature.
Definition: qgssymbol.cpp:643
void addCategory(const QgsRendererCategory &category)
The class stores information about one class/rule of a vector layer renderer in a unified way that ca...
Contains information about the context of a rendering operation.
void setSourceColorRamp(QgsColorRamp *ramp)
Sets the source color ramp.
bool usingSymbolLevels() const
Definition: qgsrenderer.h:271
QgsColorRamp * sourceColorRamp()
Returns the source color ramp, from which each categories&#39; color is derived.
QSet< QString > legendKeysForFeature(const QgsFeature &feature, QgsRenderContext &context) const override
Returns legend keys matching a specified feature.
QgsRendererCategory & operator=(QgsRendererCategory cat)
virtual void startRender(QgsRenderContext &context, const QgsFields &fields)
Must be called when a new render cycle is started.
Definition: qgsrenderer.cpp:92
virtual QgsSymbol * clone() const =0
Gets a deep copy of this symbol.
std::unique_ptr< QgsColorRamp > mSourceColorRamp
void CORE_EXPORT save(QDomElement &elem) const
Serialize to XML.
int categoryIndexForValue(const QVariant &val)
Returns index of category with specified value (-1 if not found)
bool updateCategoryValue(int catIndex, const QVariant &value)
virtual void stopRender(QgsRenderContext &context)
Must be called when a render cycle has finished, to allow the renderer to clean up.
QMap< QString, QgsSymbol *> QgsSymbolMap
Definition: qgsrenderer.h:44
bool updateCategorySymbol(int catIndex, QgsSymbol *symbol)
bool valueLessThan(const QgsRendererCategory &c1, const QgsRendererCategory &c2)
QgsSymbol * symbolForFeature(const QgsFeature &feature, QgsRenderContext &context) const override
To be overridden.
void copyRendererData(QgsFeatureRenderer *destRenderer) const
Clones generic renderer data to another renderer.
Definition: qgsrenderer.cpp:48
static QgsCategorizedSymbolRenderer * convertFromRenderer(const QgsFeatureRenderer *renderer)
creates a QgsCategorizedSymbolRenderer from an existing renderer.
QHash< QString, QgsSymbol * > mSymbolHash
hashtable for faster access to symbols
void sortByValue(Qt::SortOrder order=Qt::AscendingOrder)
void updateSymbols(QgsSymbol *sym)
Update all the symbols but leave categories and colors.
static QString quotedValue(const QVariant &value)
Returns a string representation of a literal value, including appropriate quotations where required...
const QgsFeatureRenderer * embeddedRenderer() const override
Returns the current embedded renderer (subrenderer) for this feature renderer.
QgsRendererCategory()=default
Constructor for QgsRendererCategory.
A vector of attributes.
Definition: qgsattributes.h:58
Object that keeps configuration of appearance of marker symbol&#39;s data-defined size in legend...
std::unique_ptr< QgsExpression > mExpression
int mAttrNum
attribute index (derived from attribute name in startRender)
void toSld(QDomDocument &doc, QDomElement &element, const QgsStringMap &props=QgsStringMap()) const override
used from subclasses to create SLD Rule elements following SLD v1.1 specs
void setSourceSymbol(QgsSymbol *sym)
Sets the source symbol for the renderer, which is the base symbol used for the each categories&#39; symbo...
void stopRender(QgsRenderContext &context)
Ends the rendering process.
Definition: qgssymbol.cpp:418
void setLabel(const QString &label)
static QgsDataDefinedSizeLegend * readXml(const QDomElement &elem, const QgsReadWriteContext &context) SIP_FACTORY
Creates instance from given element and returns it (caller takes ownership). Returns null on error...
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns a list of attributes required by this renderer.
void setOrderByEnabled(bool enabled)
Sets whether custom ordering should be applied before features are processed by this renderer...
QVariant::Type type
Definition: qgsfield.h:55
void setValue(const QVariant &value)
virtual QgsFeatureRenderer * clone() const =0
Create a deep copy of this renderer.
QgsAttributes attributes
Definition: qgsfeature.h:72
bool isActive() const
Returns whether the property is currently active.
std::unique_ptr< QgsDataDefinedSizeLegend > mDataDefinedSizeLegend
void setColor(const QColor &color)
Definition: qgssymbol.cpp:440
virtual bool saveProperties(QDomDocument &doc, QDomElement &element) const
Saves the current state of the effect to a DOM element.