QGIS API Documentation  3.10.0-A Coruña (6c816b4204)
qgsdiagramrenderer.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsdiagramrenderer.cpp
3  ---------------------
4  begin : March 2011
5  copyright : (C) 2011 by Marco Hugentobler
6  email : marco dot hugentobler at sourcepole dot ch
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 "qgsdiagramrenderer.h"
16 
18 #include "qgsvectorlayer.h"
19 #include "diagram/qgstextdiagram.h"
20 #include "diagram/qgspiediagram.h"
22 #include "qgsrendercontext.h"
24 #include "qgsfontutils.h"
25 #include "qgssymbollayerutils.h"
26 
27 #include <QDomElement>
28 #include <QPainter>
29 
30 QgsPropertiesDefinition QgsDiagramLayerSettings::sPropertyDefinitions;
31 
32 void QgsDiagramLayerSettings::initPropertyDefinitions()
33 {
34  if ( !sPropertyDefinitions.isEmpty() )
35  return;
36 
37  const QString origin = QStringLiteral( "diagram" );
38 
39  sPropertyDefinitions = QgsPropertiesDefinition
40  {
41  { QgsDiagramLayerSettings::BackgroundColor, QgsPropertyDefinition( "backgroundColor", QObject::tr( "Background color" ), QgsPropertyDefinition::ColorWithAlpha, origin ) },
42  { QgsDiagramLayerSettings::StrokeColor, QgsPropertyDefinition( "strokeColor", QObject::tr( "Stroke color" ), QgsPropertyDefinition::ColorWithAlpha, origin ) },
43  { QgsDiagramLayerSettings::StrokeWidth, QgsPropertyDefinition( "strokeWidth", QObject::tr( "Stroke width" ), QgsPropertyDefinition::StrokeWidth, origin ) },
44  { QgsDiagramLayerSettings::PositionX, QgsPropertyDefinition( "positionX", QObject::tr( "Position (X)" ), QgsPropertyDefinition::Double, origin ) },
45  { QgsDiagramLayerSettings::PositionY, QgsPropertyDefinition( "positionY", QObject::tr( "Position (Y)" ), QgsPropertyDefinition::Double, origin ) },
46  { QgsDiagramLayerSettings::Distance, QgsPropertyDefinition( "distance", QObject::tr( "Placement distance" ), QgsPropertyDefinition::DoublePositive, origin ) },
47  { QgsDiagramLayerSettings::Priority, QgsPropertyDefinition( "priority", QObject::tr( "Placement priority" ), QgsPropertyDefinition::DoublePositive, origin ) },
48  { QgsDiagramLayerSettings::ZIndex, QgsPropertyDefinition( "zIndex", QObject::tr( "Placement z-index" ), QgsPropertyDefinition::Double, origin ) },
49  { QgsDiagramLayerSettings::IsObstacle, QgsPropertyDefinition( "isObstacle", QObject::tr( "Diagram is an obstacle" ), QgsPropertyDefinition::Boolean, origin ) },
50  { QgsDiagramLayerSettings::Show, QgsPropertyDefinition( "show", QObject::tr( "Show diagram" ), QgsPropertyDefinition::Boolean, origin ) },
51  { QgsDiagramLayerSettings::AlwaysShow, QgsPropertyDefinition( "alwaysShow", QObject::tr( "Always show diagram" ), QgsPropertyDefinition::Boolean, origin ) },
52  { QgsDiagramLayerSettings::StartAngle, QgsPropertyDefinition( "startAngle", QObject::tr( "Pie chart start angle" ), QgsPropertyDefinition::Rotation, origin ) },
53  };
54 }
55 
57 {
58  initPropertyDefinitions();
59  return sPropertyDefinitions;
60 }
61 
63 {
64  initPropertyDefinitions();
65 }
66 
68  : mCt( rh.mCt )
69  , mPlacement( rh.mPlacement )
70  , mPlacementFlags( rh.mPlacementFlags )
71  , mPriority( rh.mPriority )
72  , mZIndex( rh.mZIndex )
73  , mObstacle( rh.mObstacle )
74  , mDistance( rh.mDistance )
75  , mRenderer( rh.mRenderer ? rh.mRenderer->clone() : nullptr )
76  , mShowAll( rh.mShowAll )
77  , mDataDefinedProperties( rh.mDataDefinedProperties )
78 {
79  initPropertyDefinitions();
80 }
81 
83 {
84  mPlacement = rh.mPlacement;
85  mPlacementFlags = rh.mPlacementFlags;
86  mPriority = rh.mPriority;
87  mZIndex = rh.mZIndex;
88  mObstacle = rh.mObstacle;
89  mDistance = rh.mDistance;
90  mRenderer = rh.mRenderer ? rh.mRenderer->clone() : nullptr;
91  mCt = rh.mCt;
92  mShowAll = rh.mShowAll;
93  mDataDefinedProperties = rh.mDataDefinedProperties;
94  return *this;
95 }
96 
98 {
99  delete mRenderer;
100 }
101 
103 {
104  if ( diagramRenderer == mRenderer )
105  return;
106 
107  delete mRenderer;
108  mRenderer = diagramRenderer;
109 }
110 
112 {
113  mCt = transform;
114 }
115 
116 void QgsDiagramLayerSettings::readXml( const QDomElement &elem )
117 {
118  QDomNodeList propertyElems = elem.elementsByTagName( QStringLiteral( "properties" ) );
119  if ( !propertyElems.isEmpty() )
120  {
121  ( void )mDataDefinedProperties.readXml( propertyElems.at( 0 ).toElement(), sPropertyDefinitions );
122  }
123  else
124  {
125  mDataDefinedProperties.clear();
126  }
127 
128  mPlacement = static_cast< Placement >( elem.attribute( QStringLiteral( "placement" ) ).toInt() );
129  mPlacementFlags = static_cast< LinePlacementFlag >( elem.attribute( QStringLiteral( "linePlacementFlags" ) ).toInt() );
130  mPriority = elem.attribute( QStringLiteral( "priority" ) ).toInt();
131  mZIndex = elem.attribute( QStringLiteral( "zIndex" ) ).toDouble();
132  mObstacle = elem.attribute( QStringLiteral( "obstacle" ) ).toInt();
133  mDistance = elem.attribute( QStringLiteral( "dist" ) ).toDouble();
134  mShowAll = ( elem.attribute( QStringLiteral( "showAll" ), QStringLiteral( "0" ) ) != QLatin1String( "0" ) );
135 }
136 
137 void QgsDiagramLayerSettings::writeXml( QDomElement &layerElem, QDomDocument &doc ) const
138 {
139  QDomElement diagramLayerElem = doc.createElement( QStringLiteral( "DiagramLayerSettings" ) );
140  QDomElement propertiesElem = doc.createElement( QStringLiteral( "properties" ) );
141  ( void )mDataDefinedProperties.writeXml( propertiesElem, sPropertyDefinitions );
142  diagramLayerElem.appendChild( propertiesElem );
143  diagramLayerElem.setAttribute( QStringLiteral( "placement" ), mPlacement );
144  diagramLayerElem.setAttribute( QStringLiteral( "linePlacementFlags" ), mPlacementFlags );
145  diagramLayerElem.setAttribute( QStringLiteral( "priority" ), mPriority );
146  diagramLayerElem.setAttribute( QStringLiteral( "zIndex" ), mZIndex );
147  diagramLayerElem.setAttribute( QStringLiteral( "obstacle" ), mObstacle );
148  diagramLayerElem.setAttribute( QStringLiteral( "dist" ), QString::number( mDistance ) );
149  diagramLayerElem.setAttribute( QStringLiteral( "showAll" ), mShowAll );
150  layerElem.appendChild( diagramLayerElem );
151 }
152 
154 {
155  return mDataDefinedProperties.prepare( context );
156 }
157 
159 {
160  QSet< QString > referenced;
161  if ( mRenderer )
162  referenced = mRenderer->referencedFields( context );
163 
164  //add the ones needed for data defined settings
165  referenced.unite( mDataDefinedProperties.referencedFields( context ) );
166 
167  return referenced;
168 }
169 
170 void QgsDiagramSettings::readXml( const QDomElement &elem )
171 {
172  enabled = ( elem.attribute( QStringLiteral( "enabled" ), QStringLiteral( "1" ) ) != QLatin1String( "0" ) );
173  if ( !QgsFontUtils::setFromXmlChildNode( font, elem, QStringLiteral( "fontProperties" ) ) )
174  {
175  font.fromString( elem.attribute( QStringLiteral( "font" ) ) );
176  }
177  backgroundColor.setNamedColor( elem.attribute( QStringLiteral( "backgroundColor" ) ) );
178  backgroundColor.setAlpha( elem.attribute( QStringLiteral( "backgroundAlpha" ) ).toInt() );
179  size.setWidth( elem.attribute( QStringLiteral( "width" ) ).toDouble() );
180  size.setHeight( elem.attribute( QStringLiteral( "height" ) ).toDouble() );
181  if ( elem.hasAttribute( QStringLiteral( "transparency" ) ) )
182  {
183  opacity = 1 - elem.attribute( QStringLiteral( "transparency" ), QStringLiteral( "0" ) ).toInt() / 255.0;
184  }
185  else
186  {
187  opacity = elem.attribute( QStringLiteral( "opacity" ), QStringLiteral( "1.00" ) ).toDouble();
188  }
189 
190  penColor.setNamedColor( elem.attribute( QStringLiteral( "penColor" ) ) );
191  int penAlpha = elem.attribute( QStringLiteral( "penAlpha" ), QStringLiteral( "255" ) ).toInt();
192  penColor.setAlpha( penAlpha );
193  penWidth = elem.attribute( QStringLiteral( "penWidth" ) ).toDouble();
194 
195  maximumScale = elem.attribute( QStringLiteral( "minScaleDenominator" ), QStringLiteral( "-1" ) ).toDouble();
196  minimumScale = elem.attribute( QStringLiteral( "maxScaleDenominator" ), QStringLiteral( "-1" ) ).toDouble();
197  if ( elem.hasAttribute( QStringLiteral( "scaleBasedVisibility" ) ) )
198  {
199  scaleBasedVisibility = ( elem.attribute( QStringLiteral( "scaleBasedVisibility" ), QStringLiteral( "1" ) ) != QLatin1String( "0" ) );
200  }
201  else
202  {
203  scaleBasedVisibility = maximumScale >= 0 && minimumScale >= 0;
204  }
205 
206  //diagram size unit type and scale
207  if ( elem.attribute( QStringLiteral( "sizeType" ) ) == QLatin1String( "MapUnits" ) )
208  {
209  //compatibility with pre-2.16 project files
210  sizeType = QgsUnitTypes::RenderMapUnits;
211  }
212  else
213  {
214  sizeType = QgsUnitTypes::decodeRenderUnit( elem.attribute( QStringLiteral( "sizeType" ) ) );
215  }
216  sizeScale = QgsSymbolLayerUtils::decodeMapUnitScale( elem.attribute( QStringLiteral( "sizeScale" ) ) );
217 
218  //line width unit type and scale
219  lineSizeUnit = QgsUnitTypes::decodeRenderUnit( elem.attribute( QStringLiteral( "lineSizeType" ) ) );
220  lineSizeScale = QgsSymbolLayerUtils::decodeMapUnitScale( elem.attribute( QStringLiteral( "lineSizeScale" ) ) );
221 
222  //label placement method
223  if ( elem.attribute( QStringLiteral( "labelPlacementMethod" ) ) == QLatin1String( "Height" ) )
224  {
225  labelPlacementMethod = Height;
226  }
227  else
228  {
229  labelPlacementMethod = XHeight;
230  }
231 
232  // orientation
233  if ( elem.attribute( QStringLiteral( "diagramOrientation" ) ) == QLatin1String( "Left" ) )
234  {
235  diagramOrientation = Left;
236  }
237  else if ( elem.attribute( QStringLiteral( "diagramOrientation" ) ) == QLatin1String( "Right" ) )
238  {
239  diagramOrientation = Right;
240  }
241  else if ( elem.attribute( QStringLiteral( "diagramOrientation" ) ) == QLatin1String( "Down" ) )
242  {
243  diagramOrientation = Down;
244  }
245  else
246  {
247  diagramOrientation = Up;
248  }
249 
250  // scale dependency
251  if ( elem.attribute( QStringLiteral( "scaleDependency" ) ) == QLatin1String( "Diameter" ) )
252  {
253  scaleByArea = false;
254  }
255  else
256  {
257  scaleByArea = true;
258  }
259 
260  barWidth = elem.attribute( QStringLiteral( "barWidth" ) ).toDouble();
261 
262  if ( elem.hasAttribute( QStringLiteral( "angleOffset" ) ) )
263  rotationOffset = std::fmod( 360.0 - elem.attribute( QStringLiteral( "angleOffset" ) ).toInt() / 16.0, 360.0 );
264  else
265  rotationOffset = elem.attribute( QStringLiteral( "rotationOffset" ) ).toDouble();
266 
267  minimumSize = elem.attribute( QStringLiteral( "minimumSize" ) ).toDouble();
268 
269  //colors
270  categoryColors.clear();
271  QDomNodeList attributes = elem.elementsByTagName( QStringLiteral( "attribute" ) );
272 
273  if ( attributes.length() > 0 )
274  {
275  for ( int i = 0; i < attributes.size(); i++ )
276  {
277  QDomElement attrElem = attributes.at( i ).toElement();
278  QColor newColor( attrElem.attribute( QStringLiteral( "color" ) ) );
279  newColor.setAlphaF( opacity );
280  categoryColors.append( newColor );
281  categoryAttributes.append( attrElem.attribute( QStringLiteral( "field" ) ) );
282  categoryLabels.append( attrElem.attribute( QStringLiteral( "label" ) ) );
283  if ( categoryLabels.back().isEmpty() )
284  {
285  categoryLabels.back() = categoryAttributes.back();
286  }
287  }
288  }
289  else
290  {
291  // Restore old format attributes and colors
292 
293  QStringList colorList = elem.attribute( QStringLiteral( "colors" ) ).split( '/' );
294  QStringList::const_iterator colorIt = colorList.constBegin();
295  for ( ; colorIt != colorList.constEnd(); ++colorIt )
296  {
297  QColor newColor( *colorIt );
298  newColor.setAlphaF( opacity );
299  categoryColors.append( QColor( newColor ) );
300  }
301 
302  //attribute indices
303  categoryAttributes.clear();
304  QStringList catList = elem.attribute( QStringLiteral( "categories" ) ).split( '/' );
305  QStringList::const_iterator catIt = catList.constBegin();
306  for ( ; catIt != catList.constEnd(); ++catIt )
307  {
308  categoryAttributes.append( *catIt );
309  categoryLabels.append( *catIt );
310  }
311  }
312 }
313 
314 void QgsDiagramSettings::writeXml( QDomElement &rendererElem, QDomDocument &doc ) const
315 {
316  QDomElement categoryElem = doc.createElement( QStringLiteral( "DiagramCategory" ) );
317  categoryElem.setAttribute( QStringLiteral( "enabled" ), enabled );
318  categoryElem.appendChild( QgsFontUtils::toXmlElement( font, doc, QStringLiteral( "fontProperties" ) ) );
319  categoryElem.setAttribute( QStringLiteral( "backgroundColor" ), backgroundColor.name() );
320  categoryElem.setAttribute( QStringLiteral( "backgroundAlpha" ), backgroundColor.alpha() );
321  categoryElem.setAttribute( QStringLiteral( "width" ), QString::number( size.width() ) );
322  categoryElem.setAttribute( QStringLiteral( "height" ), QString::number( size.height() ) );
323  categoryElem.setAttribute( QStringLiteral( "penColor" ), penColor.name() );
324  categoryElem.setAttribute( QStringLiteral( "penAlpha" ), penColor.alpha() );
325  categoryElem.setAttribute( QStringLiteral( "penWidth" ), QString::number( penWidth ) );
326  categoryElem.setAttribute( QStringLiteral( "scaleBasedVisibility" ), scaleBasedVisibility );
327  categoryElem.setAttribute( QStringLiteral( "minScaleDenominator" ), QString::number( maximumScale ) );
328  categoryElem.setAttribute( QStringLiteral( "maxScaleDenominator" ), QString::number( minimumScale ) );
329  categoryElem.setAttribute( QStringLiteral( "opacity" ), QString::number( opacity ) );
330 
331  //diagram size unit type and scale
332  categoryElem.setAttribute( QStringLiteral( "sizeType" ), QgsUnitTypes::encodeUnit( sizeType ) );
333  categoryElem.setAttribute( QStringLiteral( "sizeScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( sizeScale ) );
334 
335  //line width unit type and scale
336  categoryElem.setAttribute( QStringLiteral( "lineSizeType" ), QgsUnitTypes::encodeUnit( lineSizeUnit ) );
337  categoryElem.setAttribute( QStringLiteral( "lineSizeScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( lineSizeScale ) );
338 
339  // label placement method (text diagram)
340  if ( labelPlacementMethod == Height )
341  {
342  categoryElem.setAttribute( QStringLiteral( "labelPlacementMethod" ), QStringLiteral( "Height" ) );
343  }
344  else
345  {
346  categoryElem.setAttribute( QStringLiteral( "labelPlacementMethod" ), QStringLiteral( "XHeight" ) );
347  }
348 
349  if ( scaleByArea )
350  {
351  categoryElem.setAttribute( QStringLiteral( "scaleDependency" ), QStringLiteral( "Area" ) );
352  }
353  else
354  {
355  categoryElem.setAttribute( QStringLiteral( "scaleDependency" ), QStringLiteral( "Diameter" ) );
356  }
357 
358  // orientation (histogram)
359  switch ( diagramOrientation )
360  {
361  case Left:
362  categoryElem.setAttribute( QStringLiteral( "diagramOrientation" ), QStringLiteral( "Left" ) );
363  break;
364 
365  case Right:
366  categoryElem.setAttribute( QStringLiteral( "diagramOrientation" ), QStringLiteral( "Right" ) );
367  break;
368 
369  case Down:
370  categoryElem.setAttribute( QStringLiteral( "diagramOrientation" ), QStringLiteral( "Down" ) );
371  break;
372 
373  case Up:
374  categoryElem.setAttribute( QStringLiteral( "diagramOrientation" ), QStringLiteral( "Up" ) );
375  break;
376 
377  default:
378  categoryElem.setAttribute( QStringLiteral( "diagramOrientation" ), QStringLiteral( "Up" ) );
379  break;
380  }
381 
382  categoryElem.setAttribute( QStringLiteral( "barWidth" ), QString::number( barWidth ) );
383  categoryElem.setAttribute( QStringLiteral( "minimumSize" ), QString::number( minimumSize ) );
384  categoryElem.setAttribute( QStringLiteral( "rotationOffset" ), QString::number( rotationOffset ) );
385 
386  int nCats = std::min( categoryColors.size(), categoryAttributes.size() );
387  for ( int i = 0; i < nCats; ++i )
388  {
389  QDomElement attributeElem = doc.createElement( QStringLiteral( "attribute" ) );
390 
391  attributeElem.setAttribute( QStringLiteral( "field" ), categoryAttributes.at( i ) );
392  attributeElem.setAttribute( QStringLiteral( "color" ), categoryColors.at( i ).name() );
393  attributeElem.setAttribute( QStringLiteral( "label" ), categoryLabels.at( i ) );
394  categoryElem.appendChild( attributeElem );
395  }
396 
397  rendererElem.appendChild( categoryElem );
398 }
399 
401 {
402  if ( mDiagram.get() == d )
403  return;
404 
405  mDiagram.reset( d );
406 }
407 
409  : mDiagram( other.mDiagram ? other.mDiagram->clone() : nullptr )
410  , mShowAttributeLegend( other.mShowAttributeLegend )
411 {
412 }
413 
415 {
416  mDiagram.reset( other.mDiagram ? other.mDiagram->clone() : nullptr );
418  return *this;
419 }
420 
421 void QgsDiagramRenderer::renderDiagram( const QgsFeature &feature, QgsRenderContext &c, QPointF pos, const QgsPropertyCollection &properties ) const
422 {
423  if ( !mDiagram )
424  {
425  return;
426  }
427 
429  if ( !diagramSettings( feature, c, s ) )
430  {
431  return;
432  }
433 
434  if ( properties.hasActiveProperties() )
435  {
444  }
445 
446  mDiagram->renderDiagram( feature, c, s, pos );
447 }
448 
449 QSizeF QgsDiagramRenderer::sizeMapUnits( const QgsFeature &feature, const QgsRenderContext &c ) const
450 {
452  if ( !diagramSettings( feature, c, s ) )
453  {
454  return QSizeF();
455  }
456 
457  QSizeF size = diagramSize( feature, c );
458  if ( size.isValid() )
459  {
460  double width = c.convertToMapUnits( size.width(), s.sizeType, s.sizeScale );
461  size.rheight() *= width / size.width();
462  size.setWidth( width );
463  }
464  return size;
465 }
466 
467 QSet<QString> QgsDiagramRenderer::referencedFields( const QgsExpressionContext &context ) const
468 {
469  QSet< QString > referenced;
470 
471  if ( !mDiagram )
472  return referenced;
473 
474  const auto constDiagramAttributes = diagramAttributes();
475  for ( const QString &att : constDiagramAttributes )
476  {
477  QgsExpression *expression = mDiagram->getExpression( att, context );
478  const auto constReferencedColumns = expression->referencedColumns();
479  for ( const QString &field : constReferencedColumns )
480  {
481  referenced << field;
482  }
483  }
484  return referenced;
485 }
486 
487 void QgsDiagramRenderer::convertSizeToMapUnits( QSizeF &size, const QgsRenderContext &context ) const
488 {
489  if ( !size.isValid() )
490  {
491  return;
492  }
493 
494  double pixelToMap = context.scaleFactor() * context.mapToPixel().mapUnitsPerPixel();
495  size.rwidth() *= pixelToMap;
496  size.rheight() *= pixelToMap;
497 }
498 
499 int QgsDiagramRenderer::dpiPaintDevice( const QPainter *painter )
500 {
501  if ( painter )
502  {
503  QPaintDevice *device = painter->device();
504  if ( device )
505  {
506  return device->logicalDpiX();
507  }
508  }
509  return -1;
510 }
511 
512 void QgsDiagramRenderer::_readXml( const QDomElement &elem, const QgsReadWriteContext &context )
513 {
514  Q_UNUSED( context )
515  mDiagram.reset();
516  QString diagramType = elem.attribute( QStringLiteral( "diagramType" ) );
517  if ( diagramType == QLatin1String( "Pie" ) )
518  {
519  mDiagram.reset( new QgsPieDiagram() );
520  }
521  else if ( diagramType == QLatin1String( "Text" ) )
522  {
523  mDiagram.reset( new QgsTextDiagram() );
524  }
525  else if ( diagramType == QLatin1String( "Histogram" ) )
526  {
527  mDiagram.reset( new QgsHistogramDiagram() );
528  }
529  mShowAttributeLegend = ( elem.attribute( QStringLiteral( "attributeLegend" ), QStringLiteral( "1" ) ) != QLatin1String( "0" ) );
530 }
531 
532 void QgsDiagramRenderer::_writeXml( QDomElement &rendererElem, QDomDocument &doc, const QgsReadWriteContext &context ) const
533 {
534  Q_UNUSED( doc )
535  Q_UNUSED( context )
536 
537  if ( mDiagram )
538  {
539  rendererElem.setAttribute( QStringLiteral( "diagramType" ), mDiagram->diagramName() );
540  }
541  rendererElem.setAttribute( QStringLiteral( "attributeLegend" ), mShowAttributeLegend );
542 }
543 
545 {
546  return new QgsSingleCategoryDiagramRenderer( *this );
547 }
548 
550 {
551  Q_UNUSED( c )
552  s = mSettings;
553  return true;
554 }
555 
557 {
558  return mDiagram->diagramSize( feature.attributes(), c, mSettings );
559 }
560 
561 QList<QgsDiagramSettings> QgsSingleCategoryDiagramRenderer::diagramSettings() const
562 {
563  QList<QgsDiagramSettings> settingsList;
564  settingsList.push_back( mSettings );
565  return settingsList;
566 }
567 
568 void QgsSingleCategoryDiagramRenderer::readXml( const QDomElement &elem, const QgsReadWriteContext &context )
569 {
570  QDomElement categoryElem = elem.firstChildElement( QStringLiteral( "DiagramCategory" ) );
571  if ( categoryElem.isNull() )
572  {
573  return;
574  }
575 
576  mSettings.readXml( categoryElem );
577  _readXml( elem, context );
578 }
579 
580 void QgsSingleCategoryDiagramRenderer::writeXml( QDomElement &layerElem, QDomDocument &doc, const QgsReadWriteContext &context ) const
581 {
582  QDomElement rendererElem = doc.createElement( QStringLiteral( "SingleCategoryDiagramRenderer" ) );
583  mSettings.writeXml( rendererElem, doc );
584  _writeXml( rendererElem, doc, context );
585  layerElem.appendChild( rendererElem );
586 }
587 
588 
590 {
591  mInterpolationSettings.classificationAttributeIsExpression = false;
592 }
593 
595  : QgsDiagramRenderer( other )
596  , mSettings( other.mSettings )
597  , mInterpolationSettings( other.mInterpolationSettings )
598  , mDataDefinedSizeLegend( other.mDataDefinedSizeLegend ? new QgsDataDefinedSizeLegend( *other.mDataDefinedSizeLegend ) : nullptr )
599 {
600 }
601 
603 {
604  delete mDataDefinedSizeLegend;
605 }
606 
608 {
609  return new QgsLinearlyInterpolatedDiagramRenderer( *this );
610 }
611 
613 {
614  QList<QgsDiagramSettings> settingsList;
615  settingsList.push_back( mSettings );
616  return settingsList;
617 }
618 
620 {
621  s = mSettings;
622  s.size = diagramSize( feature, c );
623  return true;
624 }
625 
627 {
628  return mSettings.categoryAttributes;
629 }
630 
632 {
633  QSet< QString > referenced = QgsDiagramRenderer::referencedFields( context );
634  if ( mInterpolationSettings.classificationAttributeIsExpression )
635  {
636  QgsExpression *expression = mDiagram->getExpression( mInterpolationSettings.classificationAttributeExpression, context );
637  const auto constReferencedColumns = expression->referencedColumns();
638  for ( const QString &field : constReferencedColumns )
639  {
640  referenced << field;
641  }
642  }
643  else
644  {
645  referenced << mInterpolationSettings.classificationField;
646  }
647  return referenced;
648 }
649 
651 {
652  return mDiagram->diagramSize( feature, c, mSettings, mInterpolationSettings );
653 }
654 
655 void QgsLinearlyInterpolatedDiagramRenderer::readXml( const QDomElement &elem, const QgsReadWriteContext &context )
656 {
657  mInterpolationSettings.lowerValue = elem.attribute( QStringLiteral( "lowerValue" ) ).toDouble();
658  mInterpolationSettings.upperValue = elem.attribute( QStringLiteral( "upperValue" ) ).toDouble();
659  mInterpolationSettings.lowerSize.setWidth( elem.attribute( QStringLiteral( "lowerWidth" ) ).toDouble() );
660  mInterpolationSettings.lowerSize.setHeight( elem.attribute( QStringLiteral( "lowerHeight" ) ).toDouble() );
661  mInterpolationSettings.upperSize.setWidth( elem.attribute( QStringLiteral( "upperWidth" ) ).toDouble() );
662  mInterpolationSettings.upperSize.setHeight( elem.attribute( QStringLiteral( "upperHeight" ) ).toDouble() );
663  mInterpolationSettings.classificationAttributeIsExpression = elem.hasAttribute( QStringLiteral( "classificationAttributeExpression" ) );
664  if ( mInterpolationSettings.classificationAttributeIsExpression )
665  {
666  mInterpolationSettings.classificationAttributeExpression = elem.attribute( QStringLiteral( "classificationAttributeExpression" ) );
667  }
668  else
669  {
670  mInterpolationSettings.classificationField = elem.attribute( QStringLiteral( "classificationField" ) );
671  }
672  QDomElement settingsElem = elem.firstChildElement( QStringLiteral( "DiagramCategory" ) );
673  if ( !settingsElem.isNull() )
674  {
675  mSettings.readXml( settingsElem );
676  }
677 
678  delete mDataDefinedSizeLegend;
679 
680  QDomElement ddsLegendSizeElem = elem.firstChildElement( QStringLiteral( "data-defined-size-legend" ) );
681  if ( !ddsLegendSizeElem.isNull() )
682  {
683  mDataDefinedSizeLegend = QgsDataDefinedSizeLegend::readXml( ddsLegendSizeElem, context );
684  }
685  else
686  {
687  // pre-3.0 projects
688  if ( elem.attribute( QStringLiteral( "sizeLegend" ), QStringLiteral( "0" ) ) != QLatin1String( "0" ) )
689  {
690  mDataDefinedSizeLegend = new QgsDataDefinedSizeLegend();
691  QDomElement sizeLegendSymbolElem = elem.firstChildElement( QStringLiteral( "symbol" ) );
692  if ( !sizeLegendSymbolElem.isNull() && sizeLegendSymbolElem.attribute( QStringLiteral( "name" ) ) == QLatin1String( "sizeSymbol" ) )
693  {
694  mDataDefinedSizeLegend->setSymbol( QgsSymbolLayerUtils::loadSymbol<QgsMarkerSymbol>( sizeLegendSymbolElem, context ) );
695  }
696  }
697  else
698  {
699  mDataDefinedSizeLegend = nullptr;
700  }
701  }
702 
703  _readXml( elem, context );
704 }
705 
706 void QgsLinearlyInterpolatedDiagramRenderer::writeXml( QDomElement &layerElem, QDomDocument &doc, const QgsReadWriteContext &context ) const
707 {
708  QDomElement rendererElem = doc.createElement( QStringLiteral( "LinearlyInterpolatedDiagramRenderer" ) );
709  rendererElem.setAttribute( QStringLiteral( "lowerValue" ), QString::number( mInterpolationSettings.lowerValue ) );
710  rendererElem.setAttribute( QStringLiteral( "upperValue" ), QString::number( mInterpolationSettings.upperValue ) );
711  rendererElem.setAttribute( QStringLiteral( "lowerWidth" ), QString::number( mInterpolationSettings.lowerSize.width() ) );
712  rendererElem.setAttribute( QStringLiteral( "lowerHeight" ), QString::number( mInterpolationSettings.lowerSize.height() ) );
713  rendererElem.setAttribute( QStringLiteral( "upperWidth" ), QString::number( mInterpolationSettings.upperSize.width() ) );
714  rendererElem.setAttribute( QStringLiteral( "upperHeight" ), QString::number( mInterpolationSettings.upperSize.height() ) );
715  if ( mInterpolationSettings.classificationAttributeIsExpression )
716  {
717  rendererElem.setAttribute( QStringLiteral( "classificationAttributeExpression" ), mInterpolationSettings.classificationAttributeExpression );
718  }
719  else
720  {
721  rendererElem.setAttribute( QStringLiteral( "classificationField" ), mInterpolationSettings.classificationField );
722  }
723  mSettings.writeXml( rendererElem, doc );
724 
725  if ( mDataDefinedSizeLegend )
726  {
727  QDomElement ddsLegendElem = doc.createElement( QStringLiteral( "data-defined-size-legend" ) );
728  mDataDefinedSizeLegend->writeXml( ddsLegendElem, context );
729  rendererElem.appendChild( ddsLegendElem );
730  }
731 
732  _writeXml( rendererElem, doc, context );
733  layerElem.appendChild( rendererElem );
734 }
735 
736 QList< QgsLayerTreeModelLegendNode * > QgsDiagramSettings::legendItems( QgsLayerTreeLayer *nodeLayer ) const
737 {
738  QList< QgsLayerTreeModelLegendNode * > list;
739  list.reserve( categoryLabels.size() );
740  for ( int i = 0; i < categoryLabels.size(); ++i )
741  {
742  QPixmap pix( 16, 16 );
743  pix.fill( categoryColors[i] );
744  list << new QgsSimpleLegendNode( nodeLayer, categoryLabels[i], QIcon( pix ), nullptr, QStringLiteral( "diagram_%1" ).arg( QString::number( i ) ) );
745  }
746  return list;
747 }
748 
749 QList< QgsLayerTreeModelLegendNode * > QgsDiagramRenderer::legendItems( QgsLayerTreeLayer * ) const
750 {
751  return QList< QgsLayerTreeModelLegendNode * >();
752 }
753 
754 QList< QgsLayerTreeModelLegendNode * > QgsSingleCategoryDiagramRenderer::legendItems( QgsLayerTreeLayer *nodeLayer ) const
755 {
756  QList< QgsLayerTreeModelLegendNode * > nodes;
757  if ( mShowAttributeLegend )
758  nodes = mSettings.legendItems( nodeLayer );
759 
760  return nodes;
761 }
762 
763 QList< QgsLayerTreeModelLegendNode * > QgsLinearlyInterpolatedDiagramRenderer::legendItems( QgsLayerTreeLayer *nodeLayer ) const
764 {
765  QList< QgsLayerTreeModelLegendNode * > nodes;
766  if ( mShowAttributeLegend )
767  nodes = mSettings.legendItems( nodeLayer );
768 
769  if ( mDataDefinedSizeLegend && mDiagram )
770  {
771  // add size legend
772  QgsMarkerSymbol *legendSymbol = mDataDefinedSizeLegend->symbol() ? mDataDefinedSizeLegend->symbol()->clone() : QgsMarkerSymbol::createSimple( QgsStringMap() );
773  legendSymbol->setSizeUnit( mSettings.sizeType );
774  legendSymbol->setSizeMapUnitScale( mSettings.sizeScale );
775 
776  QgsDataDefinedSizeLegend ddSizeLegend( *mDataDefinedSizeLegend );
777  ddSizeLegend.setSymbol( legendSymbol ); // transfers ownership
778 
779  QList<QgsDataDefinedSizeLegend::SizeClass> sizeClasses;
780  if ( ddSizeLegend.classes().isEmpty() )
781  {
782  // automatic class creation if the classes are not defined manually
783  const auto prettyBreaks { QgsSymbolLayerUtils::prettyBreaks( mInterpolationSettings.lowerValue, mInterpolationSettings.upperValue, 4 ) };
784  for ( double v : prettyBreaks )
785  {
786  double size = mDiagram->legendSize( v, mSettings, mInterpolationSettings );
787  sizeClasses << QgsDataDefinedSizeLegend::SizeClass( size, QString::number( v ) );
788  }
789  }
790  else
791  {
792  // manual classes need to get size scaled because the QgsSizeScaleTransformer is not used in diagrams :-(
793  const auto constClasses = ddSizeLegend.classes();
794  for ( const QgsDataDefinedSizeLegend::SizeClass &sc : constClasses )
795  {
796  double size = mDiagram->legendSize( sc.size, mSettings, mInterpolationSettings );
797  sizeClasses << QgsDataDefinedSizeLegend::SizeClass( size, sc.label );
798  }
799  }
800  ddSizeLegend.setClasses( sizeClasses );
801 
802  const auto constLegendSymbolList = ddSizeLegend.legendSymbolList();
803  for ( const QgsLegendSymbolItem &si : constLegendSymbolList )
804  {
805  if ( si.dataDefinedSizeLegendSettings() )
806  nodes << new QgsDataDefinedSizeLegendNode( nodeLayer, *si.dataDefinedSizeLegendSettings() );
807  else
808  nodes << new QgsSymbolLegendNode( nodeLayer, si );
809  }
810  }
811 
812  return nodes;
813 }
814 
816 {
817  delete mDataDefinedSizeLegend;
818  mDataDefinedSizeLegend = settings;
819 }
820 
822 {
823  return mDataDefinedSizeLegend;
824 }
void _writeXml(QDomElement &rendererElem, QDomDocument &doc, const QgsReadWriteContext &context) const
Writes internal QgsDiagramRenderer diagram state to a DOM element.
Class for parsing and evaluation of expressions (formerly called "search strings").
static int dpiPaintDevice(const QPainter *)
Returns the paint device dpi (or -1 in case of error.
The class is used as a container of context for various read/write operations on other objects...
QColor valueAsColor(int key, const QgsExpressionContext &context, const QColor &defaultColor=QColor(), bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a color...
void setCoordinateTransform(const QgsCoordinateTransform &transform)
Sets the coordinate transform associated with the layer.
double convertToMapUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale()) const
Converts a size from the specified units to map units.
Distance to diagram from feature.
void writeXml(QDomElement &layerElem, QDomDocument &doc, const QgsReadWriteContext &context) const override
Writes diagram state to a DOM element.
QSet< QString > referencedFields(const QgsExpressionContext &context=QgsExpressionContext()) const override
Returns the set of any fields required for diagram rendering.
QgsSingleCategoryDiagramRenderer * clone() const override
Returns new instance that is equivalent to this one.
double valueAsDouble(int key, const QgsExpressionContext &context, double defaultValue=0.0, bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a double...
Definition of one class for the legend.
virtual bool readXml(const QDomElement &collectionElem, const QgsPropertiesDefinition &definitions)
Reads property collection state from an XML element.
Renders the diagrams for all features with the same settings.
QList< QString > categoryAttributes
QgsDiagramRenderer()=default
Constructor for QgsDiagramRenderer.
virtual QList< QgsLayerTreeModelLegendNode *> legendItems(QgsLayerTreeLayer *nodeLayer) const
Returns list of legend nodes for the diagram.
Produces legend node with a marker symbol.
virtual QList< QString > diagramAttributes() const =0
Returns attribute indices needed for diagram rendering.
void writeXml(QDomElement &layerElem, QDomDocument &doc) const
Writes the diagram settings to a DOM element.
void setSizeMapUnitScale(const QgsMapUnitScale &scale)
Sets the size map unit scale for the whole symbol (including all symbol layers).
Definition: qgssymbol.cpp:1479
Implementation of legend node interface for displaying preview of vector symbols and their labels and...
Angle offset for pie diagram.
static QString encodeMapUnitScale(const QgsMapUnitScale &mapUnitScale)
Color with alpha channel.
Definition: qgsproperty.h:64
QSet< QString > referencedFields(const QgsExpressionContext &context=QgsExpressionContext()) const
Returns the set of any fields referenced by the layer&#39;s diagrams.
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:55
Positive double value (including 0)
Definition: qgsproperty.h:58
void renderDiagram(const QgsFeature &feature, QgsRenderContext &c, QPointF pos, const QgsPropertyCollection &properties=QgsPropertyCollection()) const
Renders the diagram for a specified feature at a specific position in the passed render context...
QgsLegendSymbolList legendSymbolList() const
Generates legend symbol items according to the configuration.
Whether diagram features act as obstacles for other diagrams/labels.
QSizeF diagramSize(const QgsFeature &, const QgsRenderContext &c) const override
Returns size of the diagram (in painter units) or an invalid size in case of error.
bool mShowAttributeLegend
Whether to show an attribute legend for the diagrams.
QMap< QString, QString > QgsStringMap
Definition: qgis.h:612
double rotationOffset
Rotation offset, in degrees clockwise from horizontal.
QgsMarkerSymbol * symbol() const
Returns marker symbol that will be used to draw markers in legend.
Rotation (value between 0-360 degrees)
Definition: qgsproperty.h:60
A marker symbol type, for rendering Point and MultiPoint geometries.
Definition: qgssymbol.h:860
void setDiagram(QgsDiagram *d)
QList< QgsDiagramSettings > diagramSettings() const override
Returns list with all diagram settings in the renderer.
static QString encodeColor(const QColor &color)
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
bool prepare(const QgsExpressionContext &context=QgsExpressionContext()) const override
Prepares the collection against a specified expression context.
Z-index for diagram ordering.
QList< QgsDataDefinedSizeLegend::SizeClass > classes() const
Returns list of classes: each class is a pair of symbol size (in units used by the symbol) and label...
Evaluates and returns the diagram settings relating to a diagram for a specific feature.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
bool prepare(const QgsExpressionContext &context=QgsExpressionContext()) const
Prepares the diagrams for a specified expression context.
void readXml(const QDomElement &elem)
Reads the diagram settings from a DOM element.
void convertSizeToMapUnits(QSizeF &size, const QgsRenderContext &context) const
Converts size from mm to map units.
QgsDiagramLayerSettings()
Constructor for QgsDiagramLayerSettings.
QgsMapUnitScale sizeScale
Diagram size unit scale.
void readXml(const QDomElement &elem, const QgsReadWriteContext &context) override
Reads diagram state from a DOM element.
static bool setFromXmlChildNode(QFont &font, const QDomElement &element, const QString &childNode)
Sets the properties of a font to match the properties stored in an XML child node.
Y-coordinate data defined diagram position.
Implementation of legend node interface for displaying arbitrary label with icon. ...
void clear() override
Removes all properties from the collection.
static Q_INVOKABLE QgsUnitTypes::RenderUnit decodeRenderUnit(const QString &string, bool *ok=nullptr)
Decodes a render unit from a string.
double mapUnitsPerPixel() const
Returns current map units per pixel.
Whether to show the diagram.
Double value (including negative values)
Definition: qgsproperty.h:57
QList< QgsLayerTreeModelLegendNode *> legendItems(QgsLayerTreeLayer *nodeLayer) const
Returns list of legend nodes for the diagram.
std::unique_ptr< QgsDiagram > mDiagram
Reference to the object that does the real diagram rendering.
void setSizeUnit(QgsUnitTypes::RenderUnit unit)
Sets the size units for the whole symbol (including all symbol layers).
Definition: qgssymbol.cpp:1441
void writeXml(QDomElement &elem, const QgsReadWriteContext &context) const
Writes configuration to the given XML element.
void writeXml(QDomElement &layerElem, QDomDocument &doc, const QgsReadWriteContext &context) const override
Writes diagram state to a DOM element.
Definition for a property.
Definition: qgsproperty.h:46
Base class for all diagram types.
Definition: qgsdiagram.h:38
QgsLinearlyInterpolatedDiagramRenderer * clone() const override
Returns new instance that is equivalent to this one.
QMap< int, QgsPropertyDefinition > QgsPropertiesDefinition
Definition of available properties.
QgsUnitTypes::RenderUnit sizeType
Diagram size unit.
void setRenderer(QgsDiagramRenderer *diagramRenderer)
Sets the diagram renderer associated with the layer.
static Q_INVOKABLE QString encodeUnit(QgsUnitTypes::DistanceUnit unit)
Encodes a distance unit to a string.
QgsExpressionContext & expressionContext()
Gets the expression context.
void readXml(const QDomElement &elem)
Reads diagram settings from XML.
Stores the settings for rendering of all diagrams for a layer.
static QList< double > prettyBreaks(double minimum, double maximum, int classes)
Computes a sequence of about &#39;classes&#39; equally spaced round values which cover the range of values fr...
QSet< QString > referencedFields(const QgsExpressionContext &context=QgsExpressionContext()) const override
Returns the set of any fields referenced by the active properties from the collection.
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.
X-coordinate data defined diagram position.
QgsDataDefinedSizeLegend * dataDefinedSizeLegend() const
Returns configuration of appearance of legend.
virtual QSet< QString > referencedFields(const QgsExpressionContext &context=QgsExpressionContext()) const
Returns the set of any fields required for diagram rendering.
const QgsMapToPixel & mapToPixel() const
Returns the context&#39;s map to pixel transform, which transforms between map coordinates and device coo...
QList< QgsLayerTreeModelLegendNode *> legendItems(QgsLayerTreeLayer *nodeLayer) const override
Returns list of legend nodes for the diagram.
virtual QSizeF diagramSize(const QgsFeature &features, const QgsRenderContext &c) const =0
Returns size of the diagram (in painter units) or an invalid size in case of error.
Diagram priority (between 0 and 10)
void setSymbol(QgsMarkerSymbol *symbol SIP_TRANSFER)
Sets marker symbol that will be used to draw markers in legend.
static const QgsPropertiesDefinition & propertyDefinitions()
Returns the diagram property definitions.
QList< QString > diagramAttributes() const override
Returns attribute indices needed for diagram rendering.
QgsDiagramRenderer & operator=(const QgsDiagramRenderer &other)
virtual QList< QgsDiagramSettings > diagramSettings() const =0
Returns list with all diagram settings in the renderer.
QString classificationField
Name of the field for classification.
Class for doing transforms between two map coordinate systems.
A grouped map of multiple QgsProperty objects, each referenced by a integer key value.
static QgsMapUnitScale decodeMapUnitScale(const QString &str)
Whether the diagram should always be shown, even if it overlaps other diagrams/labels.
virtual QSizeF sizeMapUnits(const QgsFeature &feature, const QgsRenderContext &c) const
Returns size of the diagram for a feature in map units. Returns an invalid QSizeF in case of error...
bool hasActiveProperties() const override
Returns true if the collection has any active properties, or false if all properties within the colle...
void writeXml(QDomElement &rendererElem, QDomDocument &doc) const
Writes diagram settings to XML.
static QgsMarkerSymbol * createSimple(const QgsStringMap &properties)
Create a marker symbol with one symbol layer: SimpleMarker with specified properties.
Definition: qgssymbol.cpp:1229
static QDomElement toXmlElement(const QFont &font, QDomDocument &document, const QString &elementName)
Returns a DOM element containing the properties of the font.
LinePlacementFlag
Line placement flags for controlling line based placements.
void readXml(const QDomElement &elem, const QgsReadWriteContext &context) override
Reads diagram state from a DOM element.
QList< QgsLayerTreeModelLegendNode *> legendItems(QgsLayerTreeLayer *nodeLayer) const override
Returns list of legend nodes for the diagram.
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
Object that keeps configuration of appearance of marker symbol&#39;s data-defined size in legend...
virtual bool writeXml(QDomElement &collectionElem, const QgsPropertiesDefinition &definitions) const
Writes the current state of the property collection into an XML element.
void setDataDefinedSizeLegend(QgsDataDefinedSizeLegend *settings)
Configures appearance of legend.
void setOriginalValueVariable(const QVariant &value)
Sets the original value variable value for the context.
static QgsDataDefinedSizeLegend * readXml(const QDomElement &elem, const QgsReadWriteContext &context) SIP_FACTORY
Creates instance from given element and returns it (caller takes ownership). Returns nullptr on error...
QList< QgsDiagramSettings > diagramSettings() const override
Returns list with all diagram settings in the renderer.
QgsDiagramLayerSettings & operator=(const QgsDiagramLayerSettings &rh)
void _readXml(const QDomElement &elem, const QgsReadWriteContext &context)
Reads internal QgsDiagramRenderer state from a DOM element.
Stores the settings for rendering a single diagram.
QgsAttributes attributes
Definition: qgsfeature.h:65
QgsMarkerSymbol * clone() const override
Returns a deep copy of this symbol.
Definition: qgssymbol.cpp:1708
virtual QgsDiagramRenderer * clone() const =0
Returns new instance that is equivalent to this one.
void setClasses(const QList< QgsDataDefinedSizeLegend::SizeClass > &classes)
Sets list of classes: each class is a pair of symbol size (in units used by the symbol) and label...
Layer tree node points to a map layer.
QSizeF diagramSize(const QgsFeature &, const QgsRenderContext &c) const override
Returns size of the diagram (in painter units) or an invalid size in case of error.