QGIS API Documentation  3.0.2-Girona (307d082)
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 = QStringLiteral( "%1 = '%2'" )
135  .arg( attrName.replace( '\"', QLatin1String( "\"\"" ) ),
136  mValue.toString().replace( '\'', QLatin1String( "''" ) ) );
137  QgsSymbolLayerUtils::createFunctionElement( doc, ruleElem, filterFunc );
138 
139  // add the mix/max scale denoms if we got any from the callers
140  QgsSymbolLayerUtils::applyScaleDependency( doc, ruleElem, props );
141 
142  mSymbol->toSld( doc, ruleElem, props );
143 }
144 
146 
148  : QgsFeatureRenderer( QStringLiteral( "categorizedSymbol" ) )
149  , mAttrName( attrName )
150 {
151  //important - we need a deep copy of the categories list, not a shared copy. This is required because
152  //QgsRendererCategory::symbol() is marked const, and so retrieving the symbol via this method does not
153  //trigger a detachment and copy of mCategories BUT that same method CAN be used to modify a symbol in place
154  Q_FOREACH ( const QgsRendererCategory &cat, categories )
155  {
156  if ( !cat.symbol() )
157  {
158  QgsDebugMsg( "invalid symbol in a category! ignoring..." );
159  }
160  mCategories << cat;
161  }
162 }
163 
165 {
166  mSymbolHash.clear();
167 
168  for ( int i = 0; i < mCategories.size(); ++i )
169  {
170  const QgsRendererCategory &cat = mCategories.at( i );
171  mSymbolHash.insert( cat.value().toString(), ( cat.renderState() || mCounting ) ? cat.symbol() : nullptr );
172  }
173 }
174 
176 {
177  return nullptr;
178 }
179 
181 {
182  bool found = false;
183  return symbolForValue( value, found );
184 }
185 
186 QgsSymbol *QgsCategorizedSymbolRenderer::symbolForValue( const QVariant &value, bool &foundMatchingSymbol )
187 {
188  foundMatchingSymbol = false;
189 
190  // TODO: special case for int, double
191  QHash<QString, QgsSymbol *>::const_iterator it = mSymbolHash.constFind( value.isNull() ? QLatin1String( "" ) : value.toString() );
192  if ( it == mSymbolHash.constEnd() )
193  {
194  if ( mSymbolHash.isEmpty() )
195  {
196  QgsDebugMsg( "there are no hashed symbols!!!" );
197  }
198  else
199  {
200  QgsDebugMsgLevel( "attribute value not found: " + value.toString(), 3 );
201  }
202  return nullptr;
203  }
204 
205  foundMatchingSymbol = true;
206 
207  return *it;
208 }
209 
211 {
212  return originalSymbolForFeature( feature, context );
213 }
214 
215 QVariant QgsCategorizedSymbolRenderer::valueForFeature( QgsFeature &feature, QgsRenderContext &context ) const
216 {
217  QgsAttributes attrs = feature.attributes();
218  QVariant value;
219  if ( mAttrNum == -1 )
220  {
221  Q_ASSERT( mExpression );
222 
223  value = mExpression->evaluate( &context.expressionContext() );
224  }
225  else
226  {
227  value = attrs.value( mAttrNum );
228  }
229 
230  return value;
231 }
232 
234 {
235  QVariant value = valueForFeature( feature, context );
236 
237  bool foundCategory = false;
238  // find the right symbol for the category
239  QgsSymbol *symbol = symbolForValue( value, foundCategory );
240 
241  if ( !foundCategory )
242  {
243  // if no symbol found, use default symbol
244  return symbolForValue( QVariant( "" ), foundCategory );
245  }
246 
247  return symbol;
248 }
249 
250 
252 {
253  for ( int i = 0; i < mCategories.count(); i++ )
254  {
255  if ( mCategories[i].value() == val )
256  return i;
257  }
258  return -1;
259 }
260 
262 {
263  int idx = -1;
264  for ( int i = 0; i < mCategories.count(); i++ )
265  {
266  if ( mCategories[i].label() == val )
267  {
268  if ( idx != -1 )
269  return -1;
270  else
271  idx = i;
272  }
273  }
274  return idx;
275 }
276 
277 bool QgsCategorizedSymbolRenderer::updateCategoryValue( int catIndex, const QVariant &value )
278 {
279  if ( catIndex < 0 || catIndex >= mCategories.size() )
280  return false;
281  mCategories[catIndex].setValue( value );
282  return true;
283 }
284 
286 {
287  if ( catIndex < 0 || catIndex >= mCategories.size() )
288  return false;
289  mCategories[catIndex].setSymbol( symbol );
290  return true;
291 }
292 
293 bool QgsCategorizedSymbolRenderer::updateCategoryLabel( int catIndex, const QString &label )
294 {
295  if ( catIndex < 0 || catIndex >= mCategories.size() )
296  return false;
297  mCategories[catIndex].setLabel( label );
298  return true;
299 }
300 
302 {
303  if ( catIndex < 0 || catIndex >= mCategories.size() )
304  return false;
305  mCategories[catIndex].setRenderState( render );
306  return true;
307 }
308 
310 {
311  if ( !cat.symbol() )
312  {
313  QgsDebugMsg( "invalid symbol in a category! ignoring..." );
314  return;
315  }
316 
317  mCategories.append( cat );
318 }
319 
321 {
322  if ( catIndex < 0 || catIndex >= mCategories.size() )
323  return false;
324 
325  mCategories.removeAt( catIndex );
326  return true;
327 }
328 
330 {
331  mCategories.clear();
332 }
333 
335 {
336  if ( from < 0 || from >= mCategories.size() || to < 0 || to >= mCategories.size() ) return;
337  mCategories.move( from, to );
338 }
339 
341 {
342  return qgsVariantLessThan( c1.value(), c2.value() );
343 }
345 {
346  return qgsVariantGreaterThan( c1.value(), c2.value() );
347 }
348 
349 void QgsCategorizedSymbolRenderer::sortByValue( Qt::SortOrder order )
350 {
351  if ( order == Qt::AscendingOrder )
352  {
353  std::sort( mCategories.begin(), mCategories.end(), valueLessThan );
354  }
355  else
356  {
357  std::sort( mCategories.begin(), mCategories.end(), valueGreaterThan );
358  }
359 }
360 
362 {
363  return QString::localeAwareCompare( c1.label(), c2.label() ) < 0;
364 }
365 
367 {
368  return !labelLessThan( c1, c2 );
369 }
370 
371 void QgsCategorizedSymbolRenderer::sortByLabel( Qt::SortOrder order )
372 {
373  if ( order == Qt::AscendingOrder )
374  {
375  std::sort( mCategories.begin(), mCategories.end(), labelLessThan );
376  }
377  else
378  {
379  std::sort( mCategories.begin(), mCategories.end(), labelGreaterThan );
380  }
381 }
382 
384 {
385  QgsFeatureRenderer::startRender( context, fields );
386 
387  mCounting = context.rendererScale() == 0.0;
388 
389  // make sure that the hash table is up to date
390  rebuildHash();
391 
392  // find out classification attribute index from name
393  mAttrNum = fields.lookupField( mAttrName );
394  if ( mAttrNum == -1 )
395  {
396  mExpression.reset( new QgsExpression( mAttrName ) );
397  mExpression->prepare( &context.expressionContext() );
398  }
399 
400  Q_FOREACH ( const QgsRendererCategory &cat, mCategories )
401  {
402  cat.symbol()->startRender( context, fields );
403  }
404 }
405 
407 {
409 
410  Q_FOREACH ( const QgsRendererCategory &cat, mCategories )
411  {
412  cat.symbol()->stopRender( context );
413  }
414  mExpression.reset();
415 }
416 
418 {
419  QSet<QString> attributes;
420 
421  // mAttrName can contain either attribute name or an expression.
422  // Sometimes it is not possible to distinguish between those two,
423  // e.g. "a - b" can be both a valid attribute name or expression.
424  // Since we do not have access to fields here, try both options.
425  attributes << mAttrName;
426 
427  QgsExpression testExpr( mAttrName );
428  if ( !testExpr.hasParserError() )
429  attributes.unite( testExpr.referencedColumns() );
430 
431  QgsCategoryList::const_iterator catIt = mCategories.constBegin();
432  for ( ; catIt != mCategories.constEnd(); ++catIt )
433  {
434  QgsSymbol *catSymbol = catIt->symbol();
435  if ( catSymbol )
436  {
437  attributes.unite( catSymbol->usedAttributes( context ) );
438  }
439  }
440  return attributes;
441 }
442 
444 {
445  QString s = QStringLiteral( "CATEGORIZED: idx %1\n" ).arg( mAttrName );
446  for ( int i = 0; i < mCategories.count(); i++ )
447  s += mCategories[i].dump();
448  return s;
449 }
450 
452 {
454  if ( mSourceSymbol )
455  r->setSourceSymbol( mSourceSymbol->clone() );
456  if ( mSourceColorRamp )
457  {
458  r->setSourceColorRamp( mSourceColorRamp->clone() );
459  }
462 
463  copyRendererData( r );
464  return r;
465 }
466 
467 void QgsCategorizedSymbolRenderer::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
468 {
469  QgsStringMap newProps = props;
470  newProps[ QStringLiteral( "attribute" )] = mAttrName;
471 
472  // create a Rule for each range
473  for ( QgsCategoryList::const_iterator it = mCategories.constBegin(); it != mCategories.constEnd(); ++it )
474  {
475  it->toSld( doc, element, newProps );
476  }
477 }
478 
480 {
481  int attrNum = fields.lookupField( mAttrName );
482  bool isExpression = ( attrNum == -1 );
483 
484  bool hasDefault = false;
485  bool defaultActive = false;
486  bool allActive = true;
487  bool noneActive = true;
488 
489  //we need to build lists of both inactive and active values, as either list may be required
490  //depending on whether the default category is active or not
491  QString activeValues;
492  QString inactiveValues;
493 
494  Q_FOREACH ( const QgsRendererCategory &cat, mCategories )
495  {
496  if ( cat.value() == "" )
497  {
498  hasDefault = true;
499  defaultActive = cat.renderState();
500  }
501 
502  noneActive = noneActive && !cat.renderState();
503  allActive = allActive && cat.renderState();
504 
505  QVariant::Type valType = isExpression ? cat.value().type() : fields.at( attrNum ).type();
506  QString value = QgsExpression::quotedValue( cat.value(), valType );
507 
508  if ( !cat.renderState() )
509  {
510  if ( cat.value() != "" )
511  {
512  if ( !inactiveValues.isEmpty() )
513  inactiveValues.append( ',' );
514 
515  inactiveValues.append( value );
516  }
517  }
518  else
519  {
520  if ( cat.value() != "" )
521  {
522  if ( !activeValues.isEmpty() )
523  activeValues.append( ',' );
524 
525  activeValues.append( value );
526  }
527  }
528  }
529 
530  QString attr = isExpression ? mAttrName : QStringLiteral( "\"%1\"" ).arg( mAttrName );
531 
532  if ( allActive && hasDefault )
533  {
534  return QString();
535  }
536  else if ( noneActive )
537  {
538  return QStringLiteral( "FALSE" );
539  }
540  else if ( defaultActive )
541  {
542  return QStringLiteral( "(%1) NOT IN (%2) OR (%1) IS NULL" ).arg( attr, inactiveValues );
543  }
544  else
545  {
546  return QStringLiteral( "(%1) IN (%2)" ).arg( attr, activeValues );
547  }
548 }
549 
551 {
552  Q_UNUSED( context );
553  QgsSymbolList lst;
554  lst.reserve( mCategories.count() );
555  Q_FOREACH ( const QgsRendererCategory &cat, mCategories )
556  {
557  lst.append( cat.symbol() );
558  }
559  return lst;
560 }
561 
563 {
564  QDomElement symbolsElem = element.firstChildElement( QStringLiteral( "symbols" ) );
565  if ( symbolsElem.isNull() )
566  return nullptr;
567 
568  QDomElement catsElem = element.firstChildElement( QStringLiteral( "categories" ) );
569  if ( catsElem.isNull() )
570  return nullptr;
571 
572  QgsSymbolMap symbolMap = QgsSymbolLayerUtils::loadSymbols( symbolsElem, context );
573  QgsCategoryList cats;
574 
575  QDomElement catElem = catsElem.firstChildElement();
576  while ( !catElem.isNull() )
577  {
578  if ( catElem.tagName() == QLatin1String( "category" ) )
579  {
580  QVariant value = QVariant( catElem.attribute( QStringLiteral( "value" ) ) );
581  QString symbolName = catElem.attribute( QStringLiteral( "symbol" ) );
582  QString label = catElem.attribute( QStringLiteral( "label" ) );
583  bool render = catElem.attribute( QStringLiteral( "render" ) ) != QLatin1String( "false" );
584  if ( symbolMap.contains( symbolName ) )
585  {
586  QgsSymbol *symbol = symbolMap.take( symbolName );
587  cats.append( QgsRendererCategory( value, symbol, label, render ) );
588  }
589  }
590  catElem = catElem.nextSiblingElement();
591  }
592 
593  QString attrName = element.attribute( QStringLiteral( "attr" ) );
594 
596 
597  // delete symbols if there are any more
599 
600  // try to load source symbol (optional)
601  QDomElement sourceSymbolElem = element.firstChildElement( QStringLiteral( "source-symbol" ) );
602  if ( !sourceSymbolElem.isNull() )
603  {
604  QgsSymbolMap sourceSymbolMap = QgsSymbolLayerUtils::loadSymbols( sourceSymbolElem, context );
605  if ( sourceSymbolMap.contains( QStringLiteral( "0" ) ) )
606  {
607  r->setSourceSymbol( sourceSymbolMap.take( QStringLiteral( "0" ) ) );
608  }
609  QgsSymbolLayerUtils::clearSymbolMap( sourceSymbolMap );
610  }
611 
612  // try to load color ramp (optional)
613  QDomElement sourceColorRampElem = element.firstChildElement( QStringLiteral( "colorramp" ) );
614  if ( !sourceColorRampElem.isNull() && sourceColorRampElem.attribute( QStringLiteral( "name" ) ) == QLatin1String( "[source]" ) )
615  {
616  r->setSourceColorRamp( QgsSymbolLayerUtils::loadColorRamp( sourceColorRampElem ) );
617  }
618 
619  QDomElement rotationElem = element.firstChildElement( QStringLiteral( "rotation" ) );
620  if ( !rotationElem.isNull() && !rotationElem.attribute( QStringLiteral( "field" ) ).isEmpty() )
621  {
622  Q_FOREACH ( const QgsRendererCategory &cat, r->mCategories )
623  {
624  convertSymbolRotation( cat.symbol(), rotationElem.attribute( QStringLiteral( "field" ) ) );
625  }
626  if ( r->mSourceSymbol )
627  {
628  convertSymbolRotation( r->mSourceSymbol.get(), rotationElem.attribute( QStringLiteral( "field" ) ) );
629  }
630  }
631 
632  QDomElement sizeScaleElem = element.firstChildElement( QStringLiteral( "sizescale" ) );
633  if ( !sizeScaleElem.isNull() && !sizeScaleElem.attribute( QStringLiteral( "field" ) ).isEmpty() )
634  {
635  Q_FOREACH ( const QgsRendererCategory &cat, r->mCategories )
636  {
638  QgsSymbolLayerUtils::decodeScaleMethod( sizeScaleElem.attribute( QStringLiteral( "scalemethod" ) ) ),
639  sizeScaleElem.attribute( QStringLiteral( "field" ) ) );
640  }
641  if ( r->mSourceSymbol && r->mSourceSymbol->type() == QgsSymbol::Marker )
642  {
644  QgsSymbolLayerUtils::decodeScaleMethod( sizeScaleElem.attribute( QStringLiteral( "scalemethod" ) ) ),
645  sizeScaleElem.attribute( QStringLiteral( "field" ) ) );
646  }
647  }
648 
649  QDomElement ddsLegendSizeElem = element.firstChildElement( QStringLiteral( "data-defined-size-legend" ) );
650  if ( !ddsLegendSizeElem.isNull() )
651  {
652  r->mDataDefinedSizeLegend.reset( QgsDataDefinedSizeLegend::readXml( ddsLegendSizeElem, context ) );
653  }
654 
655  // TODO: symbol levels
656  return r;
657 }
658 
659 QDomElement QgsCategorizedSymbolRenderer::save( QDomDocument &doc, const QgsReadWriteContext &context )
660 {
661  // clazy:skip
662  QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
663  rendererElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "categorizedSymbol" ) );
664  rendererElem.setAttribute( QStringLiteral( "symbollevels" ), ( mUsingSymbolLevels ? QStringLiteral( "1" ) : QStringLiteral( "0" ) ) );
665  rendererElem.setAttribute( QStringLiteral( "forceraster" ), ( mForceRaster ? QStringLiteral( "1" ) : QStringLiteral( "0" ) ) );
666  rendererElem.setAttribute( QStringLiteral( "attr" ), mAttrName );
667 
668  // categories
669  if ( !mCategories.isEmpty() )
670  {
671  int i = 0;
673  QDomElement catsElem = doc.createElement( QStringLiteral( "categories" ) );
674  QgsCategoryList::const_iterator it = mCategories.constBegin();
675  for ( ; it != mCategories.constEnd(); ++it )
676  {
677  const QgsRendererCategory &cat = *it;
678  QString symbolName = QString::number( i );
679  symbols.insert( symbolName, cat.symbol() );
680 
681  QDomElement catElem = doc.createElement( QStringLiteral( "category" ) );
682  catElem.setAttribute( QStringLiteral( "value" ), cat.value().toString() );
683  catElem.setAttribute( QStringLiteral( "symbol" ), symbolName );
684  catElem.setAttribute( QStringLiteral( "label" ), cat.label() );
685  catElem.setAttribute( QStringLiteral( "render" ), cat.renderState() ? "true" : "false" );
686  catsElem.appendChild( catElem );
687  i++;
688  }
689  rendererElem.appendChild( catsElem );
690 
691  // save symbols
692  QDomElement symbolsElem = QgsSymbolLayerUtils::saveSymbols( symbols, QStringLiteral( "symbols" ), doc, context );
693  rendererElem.appendChild( symbolsElem );
694 
695  }
696 
697  // save source symbol
698  if ( mSourceSymbol )
699  {
700  QgsSymbolMap sourceSymbols;
701  sourceSymbols.insert( QStringLiteral( "0" ), mSourceSymbol.get() );
702  QDomElement sourceSymbolElem = QgsSymbolLayerUtils::saveSymbols( sourceSymbols, QStringLiteral( "source-symbol" ), doc, context );
703  rendererElem.appendChild( sourceSymbolElem );
704  }
705 
706  // save source color ramp
707  if ( mSourceColorRamp )
708  {
709  QDomElement colorRampElem = QgsSymbolLayerUtils::saveColorRamp( QStringLiteral( "[source]" ), mSourceColorRamp.get(), doc );
710  rendererElem.appendChild( colorRampElem );
711  }
712 
713  QDomElement rotationElem = doc.createElement( QStringLiteral( "rotation" ) );
714  rendererElem.appendChild( rotationElem );
715 
716  QDomElement sizeScaleElem = doc.createElement( QStringLiteral( "sizescale" ) );
717  rendererElem.appendChild( sizeScaleElem );
718 
720  mPaintEffect->saveProperties( doc, rendererElem );
721 
722  if ( !mOrderBy.isEmpty() )
723  {
724  QDomElement orderBy = doc.createElement( QStringLiteral( "orderby" ) );
725  mOrderBy.save( orderBy );
726  rendererElem.appendChild( orderBy );
727  }
728  rendererElem.setAttribute( QStringLiteral( "enableorderby" ), ( mOrderByEnabled ? QStringLiteral( "1" ) : QStringLiteral( "0" ) ) );
729 
731  {
732  QDomElement ddsLegendElem = doc.createElement( QStringLiteral( "data-defined-size-legend" ) );
733  mDataDefinedSizeLegend->writeXml( ddsLegendElem, context );
734  rendererElem.appendChild( ddsLegendElem );
735  }
736 
737  return rendererElem;
738 }
739 
740 
741 QgsLegendSymbolList QgsCategorizedSymbolRenderer::baseLegendSymbolItems() const
742 {
744  int i = 0;
745  Q_FOREACH ( const QgsRendererCategory &cat, mCategories )
746  {
747  lst << QgsLegendSymbolItem( cat.symbol(), cat.label(), QString::number( i++ ), true );
748  }
749  return lst;
750 }
751 
753 {
755  {
756  // check that all symbols that have the same size expression
757  QgsProperty ddSize;
758  Q_FOREACH ( const QgsRendererCategory &category, mCategories )
759  {
760  const QgsMarkerSymbol *symbol = static_cast<const QgsMarkerSymbol *>( category.symbol() );
761  if ( ddSize )
762  {
763  QgsProperty sSize( symbol->dataDefinedSize() );
764  if ( sSize != ddSize )
765  {
766  // no common size expression
767  return baseLegendSymbolItems();
768  }
769  }
770  else
771  {
772  ddSize = symbol->dataDefinedSize();
773  }
774  }
775 
776  if ( ddSize && ddSize.isActive() )
777  {
779 
781  ddSizeLegend.updateFromSymbolAndProperty( static_cast<const QgsMarkerSymbol *>( mSourceSymbol.get() ), ddSize );
782  lst += ddSizeLegend.legendSymbolList();
783 
784  lst += baseLegendSymbolItems();
785  return lst;
786  }
787  }
788 
789  return baseLegendSymbolItems();
790 }
791 
793 {
794  QString value = valueForFeature( feature, context ).toString();
795  int i = 0;
796 
797  Q_FOREACH ( const QgsRendererCategory &cat, mCategories )
798  {
799  if ( value == cat.value() )
800  {
801  if ( cat.renderState() || mCounting )
802  return QSet< QString >() << QString::number( i );
803  else
804  return QSet< QString >();
805  }
806  i++;
807  }
808 
809  return QSet< QString >();
810 }
811 
813 {
814  return mSourceSymbol.get();
815 }
817 {
818  mSourceSymbol.reset( sym );
819 }
820 
822 {
823  return mSourceColorRamp.get();
824 }
825 
827 {
828  mSourceColorRamp.reset( ramp );
829 }
830 
832 {
833  setSourceColorRamp( ramp );
834  double num = mCategories.count() - 1;
835  double count = 0;
836 
837  QgsRandomColorRamp *randomRamp = dynamic_cast<QgsRandomColorRamp *>( ramp );
838  if ( randomRamp )
839  {
840  //ramp is a random colors ramp, so inform it of the total number of required colors
841  //this allows the ramp to pregenerate a set of visually distinctive colors
842  randomRamp->setTotalColorCount( mCategories.count() );
843  }
844 
845  Q_FOREACH ( const QgsRendererCategory &cat, mCategories )
846  {
847  double value = count / num;
848  cat.symbol()->setColor( mSourceColorRamp->color( value ) );
849  count += 1;
850  }
851 }
852 
854 {
855  int i = 0;
856  Q_FOREACH ( const QgsRendererCategory &cat, mCategories )
857  {
858  QgsSymbol *symbol = sym->clone();
859  symbol->setColor( cat.symbol()->color() );
860  updateCategorySymbol( i, symbol );
861  ++i;
862  }
863  setSourceSymbol( sym->clone() );
864 }
865 
867 {
868  return true;
869 }
870 
872 {
873  bool ok;
874  int index = key.toInt( &ok );
875  if ( ok && index >= 0 && index < mCategories.size() )
876  return mCategories.at( index ).renderState();
877  else
878  return true;
879 }
880 
882 {
883  bool ok;
884  int index = key.toInt( &ok );
885  if ( ok )
886  updateCategorySymbol( index, symbol );
887  else
888  delete symbol;
889 }
890 
891 void QgsCategorizedSymbolRenderer::checkLegendSymbolItem( const QString &key, bool state )
892 {
893  bool ok;
894  int index = key.toInt( &ok );
895  if ( ok )
896  updateCategoryRenderState( index, state );
897 }
898 
900 {
901  QgsCategorizedSymbolRenderer *r = nullptr;
902  if ( renderer->type() == QLatin1String( "categorizedSymbol" ) )
903  {
904  r = dynamic_cast<QgsCategorizedSymbolRenderer *>( renderer->clone() );
905  }
906  else if ( renderer->type() == QLatin1String( "pointDisplacement" ) || renderer->type() == QLatin1String( "pointCluster" ) )
907  {
908  const QgsPointDistanceRenderer *pointDistanceRenderer = dynamic_cast<const QgsPointDistanceRenderer *>( renderer );
909  if ( pointDistanceRenderer )
910  r = convertFromRenderer( pointDistanceRenderer->embeddedRenderer() );
911  }
912  else if ( renderer->type() == QLatin1String( "invertedPolygonRenderer" ) )
913  {
914  const QgsInvertedPolygonRenderer *invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer *>( renderer );
915  if ( invertedPolygonRenderer )
916  r = convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() );
917  }
918 
919  // If not one of the specifically handled renderers, then just grab the symbol from the renderer
920  // Could have applied this to specific renderer types (singleSymbol, graduatedSymbol)
921 
922  if ( !r )
923  {
924  r = new QgsCategorizedSymbolRenderer( QLatin1String( "" ), QgsCategoryList() );
925  QgsRenderContext context;
926  QgsSymbolList symbols = const_cast<QgsFeatureRenderer *>( renderer )->symbols( context );
927  if ( !symbols.isEmpty() )
928  {
929  r->setSourceSymbol( symbols.at( 0 )->clone() );
930  }
931  }
932 
933  r->setOrderBy( renderer->orderBy() );
934  r->setOrderByEnabled( renderer->orderByEnabled() );
935 
936  return r;
937 }
938 
940 {
941  mDataDefinedSizeLegend.reset( settings );
942 }
943 
945 {
946  return mDataDefinedSizeLegend.get();
947 }
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
QgsSymbol * originalSymbolForFeature(QgsFeature &feature, QgsRenderContext &context) override
Return symbol for feature.
Q_DECL_DEPRECATED QgsSymbol * skipRender()
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.
static QgsSymbol::ScaleMethod decodeScaleMethod(const QString &str)
QList< QgsLegendSymbolItem > QgsLegendSymbolList
QgsFeatureRequest::OrderBy mOrderBy
Definition: qgsrenderer.h:517
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
Get 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:269
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.
Q_DECL_DEPRECATED QgsSymbol * symbolForValue(const QVariant &value)
Returns the matching symbol corresponding to an attribute value.
QgsPaintEffect * mPaintEffect
Definition: qgsrenderer.h:501
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)
return 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:479
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:401
QgsField at(int i) const
Get 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
virtual void setTotalColorCount(const int colorCount)
Sets the desired total number of unique colors for the resultant ramp.
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:126
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
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:454
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.
QSet< QString > legendKeysForFeature(QgsFeature &feature, QgsRenderContext &context) override
Return legend keys matching a specified feature.
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
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
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:1379
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.
QgsSymbol * symbolForFeature(QgsFeature &feature, QgsRenderContext &context) override
To be overridden.
QSet< QString > usedAttributes(const QgsRenderContext &context) const
Return a list of attributes required to render this feature.
Definition: qgssymbol.cpp:648
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:268
QgsColorRamp * sourceColorRamp()
Returns the source color ramp, from which each categories&#39; color is derived.
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
Get 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)
return 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
QgsSymbolList symbols(QgsRenderContext &context) override
Returns list of symbols used by the renderer.
bool updateCategorySymbol(int catIndex, QgsSymbol *symbol)
bool valueLessThan(const QgsRendererCategory &c1, const QgsRendererCategory &c2)
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.
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:423
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
Return 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:445
virtual bool saveProperties(QDomDocument &doc, QDomElement &element) const
Saves the current state of the effect to a DOM element.