QGIS API Documentation 4.0.0-Norrköping (1ddcee3d0e4)
Loading...
Searching...
No Matches
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
17#include <memory>
18
24#include "qgsapplication.h"
25#include "qgscolorutils.h"
27#include "qgsfontutils.h"
29#include "qgslinesymbol.h"
30#include "qgsmarkersymbol.h"
31#include "qgspainteffect.h"
33#include "qgsrendercontext.h"
34#include "qgsscaleutils.h"
35#include "qgssymbollayerutils.h"
36#include "qgsunittypes.h"
37
38#include <QDomElement>
39#include <QPainter>
40#include <QString>
41
42using namespace Qt::StringLiterals;
43
44QgsPropertiesDefinition QgsDiagramLayerSettings::sPropertyDefinitions;
45
46void QgsDiagramLayerSettings::initPropertyDefinitions()
47{
48 if ( !sPropertyDefinitions.isEmpty() )
49 return;
50
51 const QString origin = u"diagram"_s;
52
53 sPropertyDefinitions = QgsPropertiesDefinition {
55 QgsPropertyDefinition( "backgroundColor", QObject::tr( "Background color" ), QgsPropertyDefinition::ColorWithAlpha, origin ) },
56 { static_cast< int >( QgsDiagramLayerSettings::Property::StrokeColor ), QgsPropertyDefinition( "strokeColor", QObject::tr( "Stroke color" ), QgsPropertyDefinition::ColorWithAlpha, origin ) },
57 { static_cast< int >( QgsDiagramLayerSettings::Property::StrokeWidth ), QgsPropertyDefinition( "strokeWidth", QObject::tr( "Stroke width" ), QgsPropertyDefinition::StrokeWidth, origin ) },
58 { static_cast< int >( QgsDiagramLayerSettings::Property::PositionX ), QgsPropertyDefinition( "positionX", QObject::tr( "Position (X)" ), QgsPropertyDefinition::Double, origin ) },
59 { static_cast< int >( QgsDiagramLayerSettings::Property::PositionY ), QgsPropertyDefinition( "positionY", QObject::tr( "Position (Y)" ), QgsPropertyDefinition::Double, origin ) },
60 { static_cast< int >( QgsDiagramLayerSettings::Property::Distance ), QgsPropertyDefinition( "distance", QObject::tr( "Placement distance" ), QgsPropertyDefinition::DoublePositive, origin ) },
61 { static_cast< int >( QgsDiagramLayerSettings::Property::Priority ), QgsPropertyDefinition( "priority", QObject::tr( "Placement priority" ), QgsPropertyDefinition::DoublePositive, origin ) },
62 { static_cast< int >( QgsDiagramLayerSettings::Property::ZIndex ), QgsPropertyDefinition( "zIndex", QObject::tr( "Placement z-index" ), QgsPropertyDefinition::Double, origin ) },
63 { static_cast< int >( QgsDiagramLayerSettings::Property::IsObstacle ), QgsPropertyDefinition( "isObstacle", QObject::tr( "Diagram is an obstacle" ), QgsPropertyDefinition::Boolean, origin ) },
64 { static_cast< int >( QgsDiagramLayerSettings::Property::Show ), QgsPropertyDefinition( "show", QObject::tr( "Show diagram" ), QgsPropertyDefinition::Boolean, origin ) },
65 { static_cast< int >( QgsDiagramLayerSettings::Property::AlwaysShow ), QgsPropertyDefinition( "alwaysShow", QObject::tr( "Always show diagram" ), QgsPropertyDefinition::Boolean, origin ) },
66 { static_cast< int >( QgsDiagramLayerSettings::Property::StartAngle ), QgsPropertyDefinition( "startAngle", QObject::tr( "Pie chart start angle" ), QgsPropertyDefinition::Rotation, origin ) },
67 };
68}
69
71{
72 initPropertyDefinitions();
73 return sPropertyDefinitions;
74}
75
77{
78 initPropertyDefinitions();
79}
80
82 //****** IMPORTANT! editing this? make sure you update the move constructor too! *****
83 : mCt( rh.mCt )
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 , mShowAll( rh.mShowAll )
92 , mDataDefinedProperties( rh.mDataDefinedProperties )
93//****** IMPORTANT! editing this? make sure you update the move constructor too! *****
94{
95 initPropertyDefinitions();
96}
97
99 : mCt( std::move( rh.mCt ) )
100 , mPlacement( rh.mPlacement )
101 , mPlacementFlags( rh.mPlacementFlags )
102 , mPriority( rh.mPriority )
103 , mZIndex( rh.mZIndex )
104 , mObstacle( rh.mObstacle )
105 , mDistance( rh.mDistance )
106 , mRenderer( std::move( rh.mRenderer ) )
107 , mShowAll( rh.mShowAll )
108 , mDataDefinedProperties( std::move( rh.mDataDefinedProperties ) )
109{}
110
112{
113 if ( &rh == this )
114 return *this;
115
116 //****** IMPORTANT! editing this? make sure you update the move assignment operator too! *****
117 mPlacement = rh.mPlacement;
118 mPlacementFlags = rh.mPlacementFlags;
119 mPriority = rh.mPriority;
120 mZIndex = rh.mZIndex;
121 mObstacle = rh.mObstacle;
122 mDistance = rh.mDistance;
123 mRenderer.reset( rh.mRenderer ? rh.mRenderer->clone() : nullptr );
124 mCt = rh.mCt;
125 mShowAll = rh.mShowAll;
126 mDataDefinedProperties = rh.mDataDefinedProperties;
127 //****** IMPORTANT! editing this? make sure you update the move assignment operator too! *****
128 return *this;
129}
130
132{
133 if ( &rh == this )
134 return *this;
135
136 mPlacement = rh.mPlacement;
137 mPlacementFlags = rh.mPlacementFlags;
138 mPriority = rh.mPriority;
139 mZIndex = rh.mZIndex;
140 mObstacle = rh.mObstacle;
141 mDistance = rh.mDistance;
142 mRenderer = std::move( rh.mRenderer );
143 mCt = std::move( rh.mCt );
144 mShowAll = rh.mShowAll;
145 mDataDefinedProperties = std::move( rh.mDataDefinedProperties );
146 return *this;
147}
148
151
153{
154 if ( diagramRenderer == mRenderer.get() )
155 return;
156
157 mRenderer.reset( diagramRenderer );
158}
159
161{
162 mCt = transform;
163}
164
165void QgsDiagramLayerSettings::readXml( const QDomElement &elem )
166{
167 const QDomNodeList propertyElems = elem.elementsByTagName( u"properties"_s );
168 if ( !propertyElems.isEmpty() )
169 {
170 ( void ) mDataDefinedProperties.readXml( propertyElems.at( 0 ).toElement(), sPropertyDefinitions );
171 }
172 else
173 {
174 mDataDefinedProperties.clear();
175 }
176
177 mPlacement = static_cast< Placement >( elem.attribute( u"placement"_s ).toInt() );
178 mPlacementFlags = static_cast< LinePlacementFlag >( elem.attribute( u"linePlacementFlags"_s ).toInt() );
179 mPriority = elem.attribute( u"priority"_s ).toInt();
180 mZIndex = elem.attribute( u"zIndex"_s ).toDouble();
181 mObstacle = elem.attribute( u"obstacle"_s ).toInt();
182 mDistance = elem.attribute( u"dist"_s ).toDouble();
183 mShowAll = ( elem.attribute( u"showAll"_s, u"0"_s ) != "0"_L1 );
184}
185
186void QgsDiagramLayerSettings::writeXml( QDomElement &layerElem, QDomDocument &doc ) const
187{
188 QDomElement diagramLayerElem = doc.createElement( u"DiagramLayerSettings"_s );
189 QDomElement propertiesElem = doc.createElement( u"properties"_s );
190 ( void ) mDataDefinedProperties.writeXml( propertiesElem, sPropertyDefinitions );
191 diagramLayerElem.appendChild( propertiesElem );
192 diagramLayerElem.setAttribute( u"placement"_s, mPlacement );
193 diagramLayerElem.setAttribute( u"linePlacementFlags"_s, mPlacementFlags );
194 diagramLayerElem.setAttribute( u"priority"_s, mPriority );
195 diagramLayerElem.setAttribute( u"zIndex"_s, mZIndex );
196 diagramLayerElem.setAttribute( u"obstacle"_s, mObstacle );
197 diagramLayerElem.setAttribute( u"dist"_s, QString::number( mDistance ) );
198 diagramLayerElem.setAttribute( u"showAll"_s, mShowAll );
199 layerElem.appendChild( diagramLayerElem );
200}
201
203{
204 return mDataDefinedProperties.prepare( context );
205}
206
208{
209 QSet< QString > referenced;
210 if ( mRenderer )
211 referenced = mRenderer->referencedFields( context );
212
213 //add the ones needed for data defined settings
214 referenced.unite( mDataDefinedProperties.referencedFields( context ) );
215
216 return referenced;
217}
218
219void QgsDiagramSettings::readXml( const QDomElement &elem, const QgsReadWriteContext &context )
220{
221 enabled = ( elem.attribute( u"enabled"_s, u"1"_s ) != "0"_L1 );
222 if ( !QgsFontUtils::setFromXmlChildNode( font, elem, u"fontProperties"_s ) )
223 {
224 font.fromString( elem.attribute( u"font"_s ) );
225 }
226 backgroundColor.setNamedColor( elem.attribute( u"backgroundColor"_s ) );
227 backgroundColor.setAlpha( elem.attribute( u"backgroundAlpha"_s ).toInt() );
228 size.setWidth( elem.attribute( u"width"_s ).toDouble() );
229 size.setHeight( elem.attribute( u"height"_s ).toDouble() );
230 if ( elem.hasAttribute( u"transparency"_s ) )
231 {
232 opacity = 1 - elem.attribute( u"transparency"_s, u"0"_s ).toInt() / 255.0;
233 }
234 else
235 {
236 opacity = elem.attribute( u"opacity"_s, u"1.00"_s ).toDouble();
237 }
238
239 penColor.setNamedColor( elem.attribute( u"penColor"_s ) );
240 const int penAlpha = elem.attribute( u"penAlpha"_s, u"255"_s ).toInt();
241 penColor.setAlpha( penAlpha );
242 penWidth = elem.attribute( u"penWidth"_s ).toDouble();
243
244 mDirection = static_cast< Direction >( elem.attribute( u"direction"_s, u"1"_s ).toInt() );
245
246 maximumScale = elem.attribute( u"minScaleDenominator"_s, u"-1"_s ).toDouble();
247 minimumScale = elem.attribute( u"maxScaleDenominator"_s, u"-1"_s ).toDouble();
248 if ( elem.hasAttribute( u"scaleBasedVisibility"_s ) )
249 {
250 scaleBasedVisibility = ( elem.attribute( u"scaleBasedVisibility"_s, u"1"_s ) != "0"_L1 );
251 }
252 else
253 {
255 }
256
257 //diagram size unit type and scale
258 if ( elem.attribute( u"sizeType"_s ) == "MapUnits"_L1 )
259 {
260 //compatibility with pre-2.16 project files
262 }
263 else
264 {
265 sizeType = QgsUnitTypes::decodeRenderUnit( elem.attribute( u"sizeType"_s ) );
266 }
267 sizeScale = QgsSymbolLayerUtils::decodeMapUnitScale( elem.attribute( u"sizeScale"_s ) );
268
269 //line width unit type and scale
270 lineSizeUnit = QgsUnitTypes::decodeRenderUnit( elem.attribute( u"lineSizeType"_s ) );
271 lineSizeScale = QgsSymbolLayerUtils::decodeMapUnitScale( elem.attribute( u"lineSizeScale"_s ) );
272
273 mSpacing = elem.attribute( u"spacing"_s ).toDouble();
274 mSpacingUnit = QgsUnitTypes::decodeRenderUnit( elem.attribute( u"spacingUnit"_s ) );
275 mSpacingMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( elem.attribute( u"spacingUnitScale"_s ) );
276
277 mStackedDiagramSpacing = elem.attribute( u"stackedDiagramSpacing"_s ).toDouble();
278 mStackedDiagramSpacingUnit = QgsUnitTypes::decodeRenderUnit( elem.attribute( u"stackedDiagramSpacingUnit"_s ) );
279 mStackedDiagramSpacingMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( elem.attribute( u"stackedDiagramSpacingUnitScale"_s ) );
280
281 //label placement method
282 if ( elem.attribute( u"labelPlacementMethod"_s ) == "Height"_L1 )
283 {
285 }
286 else
287 {
289 }
290
291 // orientation
292 if ( elem.attribute( u"diagramOrientation"_s ) == "Left"_L1 )
293 {
295 }
296 else if ( elem.attribute( u"diagramOrientation"_s ) == "Right"_L1 )
297 {
299 }
300 else if ( elem.attribute( u"diagramOrientation"_s ) == "Down"_L1 )
301 {
303 }
304 else
305 {
307 }
308
309 // stacked mode
310 if ( elem.attribute( u"stackedDiagramMode"_s ) == "Horizontal"_L1 )
311 {
313 }
314 else if ( elem.attribute( u"stackedDiagramMode"_s ) == "Vertical"_L1 )
315 {
317 }
318
319 // scale dependency
320 if ( elem.attribute( u"scaleDependency"_s ) == "Diameter"_L1 )
321 {
322 scaleByArea = false;
323 }
324 else
325 {
326 scaleByArea = true;
327 }
328
329 barWidth = elem.attribute( u"barWidth"_s ).toDouble();
330
331 if ( elem.hasAttribute( u"angleOffset"_s ) )
332 rotationOffset = std::fmod( 360.0 - elem.attribute( u"angleOffset"_s ).toInt() / 16.0, 360.0 );
333 else
334 rotationOffset = elem.attribute( u"rotationOffset"_s ).toDouble();
335
336 minimumSize = elem.attribute( u"minimumSize"_s ).toDouble();
337
338 const QDomNodeList axisSymbolNodes = elem.elementsByTagName( u"axisSymbol"_s );
339 if ( axisSymbolNodes.count() > 0 )
340 {
341 const QDomElement axisSymbolElem = axisSymbolNodes.at( 0 ).toElement().firstChildElement();
342 mAxisLineSymbol = QgsSymbolLayerUtils::loadSymbol<QgsLineSymbol>( axisSymbolElem, context );
343 }
344 else
345 {
346 mAxisLineSymbol = std::make_unique< QgsLineSymbol >();
347 }
348
349 mShowAxis = elem.attribute( u"showAxis"_s, u"0"_s ).toInt();
350
351 //colors
352 categoryColors.clear();
353 const QDomNodeList attributes = elem.elementsByTagName( u"attribute"_s );
354
355
356 if ( attributes.length() > 0 )
357 {
358 for ( int i = 0; i < attributes.size(); i++ )
359 {
360 const QDomElement attrElem = attributes.at( i ).toElement();
361 QColor newColor( attrElem.attribute( u"color"_s ) );
362 newColor.setAlphaF( attrElem.attribute( u"colorOpacity"_s, u"1.0"_s ).toDouble() );
363 categoryColors.append( newColor );
364 categoryAttributes.append( attrElem.attribute( u"field"_s ) );
365 categoryLabels.append( attrElem.attribute( u"label"_s ) );
366 if ( categoryLabels.constLast().isEmpty() )
367 {
368 categoryLabels.back() = categoryAttributes.back();
369 }
370 }
371 }
372 else
373 {
374 // Restore old format attributes and colors
375
376 const QStringList colorList = elem.attribute( u"colors"_s ).split( '/' );
377 QStringList::const_iterator colorIt = colorList.constBegin();
378 for ( ; colorIt != colorList.constEnd(); ++colorIt )
379 {
380 QColor newColor( *colorIt );
381 categoryColors.append( QColor( newColor ) );
382 }
383
384 //attribute indices
385 categoryAttributes.clear();
386 const QStringList catList = elem.attribute( u"categories"_s ).split( '/' );
387 QStringList::const_iterator catIt = catList.constBegin();
388 for ( ; catIt != catList.constEnd(); ++catIt )
389 {
390 categoryAttributes.append( *catIt );
391 categoryLabels.append( *catIt );
392 }
393 }
394
395 const QDomElement effectElem = elem.firstChildElement( u"effect"_s );
396 if ( !effectElem.isNull() )
397 setPaintEffect( QgsApplication::paintEffectRegistry()->createEffect( effectElem ) );
398 else
400}
401
402void QgsDiagramSettings::writeXml( QDomElement &rendererElem, QDomDocument &doc, const QgsReadWriteContext &context ) const
403{
404 QDomElement categoryElem = doc.createElement( u"DiagramCategory"_s );
405 categoryElem.setAttribute( u"enabled"_s, enabled );
406 categoryElem.appendChild( QgsFontUtils::toXmlElement( font, doc, u"fontProperties"_s ) );
407 categoryElem.setAttribute( u"backgroundColor"_s, backgroundColor.name() );
408 categoryElem.setAttribute( u"backgroundAlpha"_s, backgroundColor.alpha() );
409 categoryElem.setAttribute( u"width"_s, QString::number( size.width() ) );
410 categoryElem.setAttribute( u"height"_s, QString::number( size.height() ) );
411 categoryElem.setAttribute( u"penColor"_s, penColor.name() );
412 categoryElem.setAttribute( u"penAlpha"_s, penColor.alpha() );
413 categoryElem.setAttribute( u"penWidth"_s, QString::number( penWidth ) );
414 categoryElem.setAttribute( u"scaleBasedVisibility"_s, scaleBasedVisibility );
415 categoryElem.setAttribute( u"minScaleDenominator"_s, QString::number( maximumScale ) );
416 categoryElem.setAttribute( u"maxScaleDenominator"_s, QString::number( minimumScale ) );
417 categoryElem.setAttribute( u"opacity"_s, QString::number( opacity ) );
418 categoryElem.setAttribute( u"spacing"_s, QString::number( mSpacing ) );
419 categoryElem.setAttribute( u"spacingUnit"_s, QgsUnitTypes::encodeUnit( mSpacingUnit ) );
420 categoryElem.setAttribute( u"spacingUnitScale"_s, QgsSymbolLayerUtils::encodeMapUnitScale( mSpacingMapUnitScale ) );
421 categoryElem.setAttribute( u"stackedDiagramSpacing"_s, QString::number( mStackedDiagramSpacing ) );
422 categoryElem.setAttribute( u"stackedDiagramSpacingUnit"_s, QgsUnitTypes::encodeUnit( mStackedDiagramSpacingUnit ) );
423 categoryElem.setAttribute( u"stackedDiagramSpacingUnitScale"_s, QgsSymbolLayerUtils::encodeMapUnitScale( mStackedDiagramSpacingMapUnitScale ) );
424 categoryElem.setAttribute( u"direction"_s, QString::number( mDirection ) );
425
426 //diagram size unit type and scale
427 categoryElem.setAttribute( u"sizeType"_s, QgsUnitTypes::encodeUnit( sizeType ) );
428 categoryElem.setAttribute( u"sizeScale"_s, QgsSymbolLayerUtils::encodeMapUnitScale( sizeScale ) );
429
430 //line width unit type and scale
431 categoryElem.setAttribute( u"lineSizeType"_s, QgsUnitTypes::encodeUnit( lineSizeUnit ) );
432 categoryElem.setAttribute( u"lineSizeScale"_s, QgsSymbolLayerUtils::encodeMapUnitScale( lineSizeScale ) );
433
434 // label placement method (text diagram)
436 {
437 categoryElem.setAttribute( u"labelPlacementMethod"_s, u"Height"_s );
438 }
439 else
440 {
441 categoryElem.setAttribute( u"labelPlacementMethod"_s, u"XHeight"_s );
442 }
443
444 if ( scaleByArea )
445 {
446 categoryElem.setAttribute( u"scaleDependency"_s, u"Area"_s );
447 }
448 else
449 {
450 categoryElem.setAttribute( u"scaleDependency"_s, u"Diameter"_s );
451 }
452
453 // orientation (histogram)
454 switch ( diagramOrientation )
455 {
456 case Left:
457 categoryElem.setAttribute( u"diagramOrientation"_s, u"Left"_s );
458 break;
459
460 case Right:
461 categoryElem.setAttribute( u"diagramOrientation"_s, u"Right"_s );
462 break;
463
464 case Down:
465 categoryElem.setAttribute( u"diagramOrientation"_s, u"Down"_s );
466 break;
467
468 case Up:
469 categoryElem.setAttribute( u"diagramOrientation"_s, u"Up"_s );
470 break;
471 }
472
473 // stacked mode
474 switch ( stackedDiagramMode )
475 {
476 case Horizontal:
477 categoryElem.setAttribute( u"stackedDiagramMode"_s, u"Horizontal"_s );
478 break;
479
480 case Vertical:
481 categoryElem.setAttribute( u"stackedDiagramMode"_s, u"Vertical"_s );
482 break;
483 }
484
485 categoryElem.setAttribute( u"barWidth"_s, QString::number( barWidth ) );
486 categoryElem.setAttribute( u"minimumSize"_s, QString::number( minimumSize ) );
487 categoryElem.setAttribute( u"rotationOffset"_s, QString::number( rotationOffset ) );
488
489 const int nCats = std::min( categoryColors.size(), categoryAttributes.size() );
490 for ( int i = 0; i < nCats; ++i )
491 {
492 QDomElement attributeElem = doc.createElement( u"attribute"_s );
493
494 attributeElem.setAttribute( u"field"_s, categoryAttributes.at( i ) );
495 attributeElem.setAttribute( u"color"_s, categoryColors.at( i ).name() );
496 attributeElem.setAttribute( u"colorOpacity"_s, QString::number( categoryColors.at( i ).alphaF() ) );
497 attributeElem.setAttribute( u"label"_s, categoryLabels.at( i ) );
498 categoryElem.appendChild( attributeElem );
499 }
500
501 categoryElem.setAttribute( u"showAxis"_s, mShowAxis ? u"1"_s : u"0"_s );
502 QDomElement axisSymbolElem = doc.createElement( u"axisSymbol"_s );
503 const QDomElement symbolElem = QgsSymbolLayerUtils::saveSymbol( QString(), mAxisLineSymbol.get(), doc, context );
504 axisSymbolElem.appendChild( symbolElem );
505 categoryElem.appendChild( axisSymbolElem );
506
507 if ( mPaintEffect && !QgsPaintEffectRegistry::isDefaultStack( mPaintEffect.get() ) )
508 mPaintEffect->saveProperties( doc, categoryElem );
509
510 rendererElem.appendChild( categoryElem );
511}
512
514{
515 if ( mDiagram.get() == d )
516 return;
517
518 mDiagram.reset( d );
519}
520
525
527{
528 if ( &other == this )
529 return *this;
530
531 mDiagram.reset( other.mDiagram ? other.mDiagram->clone() : nullptr );
533 return *this;
534}
535
536void QgsDiagramRenderer::renderDiagram( const QgsFeature &feature, QgsRenderContext &c, QPointF pos, const QgsPropertyCollection &properties ) const
537{
538 if ( !mDiagram )
539 {
540 return;
541 }
542
544 if ( !diagramSettings( feature, c, s ) )
545 {
546 return;
547 }
548
549 if ( properties.hasActiveProperties() )
550 {
551 c.expressionContext().setOriginalValueVariable( QgsColorUtils::colorToString( s.backgroundColor ) );
553 c.expressionContext().setOriginalValueVariable( QgsColorUtils::colorToString( s.penColor ) );
554 s.penColor = properties.valueAsColor( QgsDiagramLayerSettings::Property::StrokeColor, c.expressionContext(), s.penColor );
555 c.expressionContext().setOriginalValueVariable( s.penWidth );
556 s.penWidth = properties.valueAsDouble( QgsDiagramLayerSettings::Property::StrokeWidth, c.expressionContext(), s.penWidth );
557 c.expressionContext().setOriginalValueVariable( s.rotationOffset );
559 }
560
561 QgsPaintEffect *effect = s.paintEffect();
562 std::unique_ptr< QgsEffectPainter > effectPainter;
563 if ( effect && effect->enabled() )
564 {
565 effectPainter = std::make_unique< QgsEffectPainter >( c, effect );
566 }
567
568 mDiagram->renderDiagram( feature, c, s, pos );
569}
570
571QSizeF QgsDiagramRenderer::sizeMapUnits( const QgsFeature &feature, const QgsRenderContext &c ) const
572{
574 if ( !diagramSettings( feature, c, s ) || !s.enabled )
575 {
576 return QSizeF();
577 }
578
579 if ( s.scaleBasedVisibility )
580 {
581 // Note: scale might be a non-round number, so compare with qgsDoubleNear
582 const double rendererScale = c.rendererScale();
583
584 // maxScale is inclusive ( < --> no size )
585 double maxScale = s.maximumScale;
586 if ( maxScale > 0 && QgsScaleUtils::lessThanMaximumScale( rendererScale, maxScale ) )
587 {
588 return QSizeF();
589 }
590
591 // minScale is exclusive ( >= --> no size)
592 double minScale = s.minimumScale;
593 if ( minScale > 0 && QgsScaleUtils::equalToOrGreaterThanMinimumScale( rendererScale, minScale ) )
594 {
595 return QSizeF();
596 }
597 }
598
599 QSizeF size = diagramSize( feature, c );
600 if ( size.isValid() )
601 {
602 const double width = c.convertToMapUnits( size.width(), s.sizeType, s.sizeScale );
603 size.rheight() *= width / size.width();
604 size.setWidth( width );
605 }
606 return size;
607}
608
610{
611 QSet< QString > referenced;
612
613 if ( !mDiagram )
614 return referenced;
615
616 const auto constDiagramAttributes = diagramAttributes();
617 for ( const QString &att : constDiagramAttributes )
618 {
619 QgsExpression *expression = mDiagram->getExpression( att, context );
620 const auto constReferencedColumns = expression->referencedColumns();
621 for ( const QString &field : constReferencedColumns )
622 {
623 referenced << field;
624 }
625 }
626 return referenced;
627}
628
629void QgsDiagramRenderer::convertSizeToMapUnits( QSizeF &size, const QgsRenderContext &context ) const
630{
631 if ( !size.isValid() )
632 {
633 return;
634 }
635
636 const double pixelToMap = context.scaleFactor() * context.mapToPixel().mapUnitsPerPixel();
637 size.rwidth() *= pixelToMap;
638 size.rheight() *= pixelToMap;
639}
640
641int QgsDiagramRenderer::dpiPaintDevice( const QPainter *painter )
642{
643 if ( painter )
644 {
645 QPaintDevice *device = painter->device();
646 if ( device )
647 {
648 return device->logicalDpiX();
649 }
650 }
651 return -1;
652}
653
654void QgsDiagramRenderer::_readXml( const QDomElement &elem, const QgsReadWriteContext &context )
655{
656 Q_UNUSED( context )
657 mDiagram.reset();
658 const QString diagramType = elem.attribute( u"diagramType"_s );
659 if ( diagramType == QgsPieDiagram::DIAGRAM_NAME_PIE )
660 {
661 mDiagram = std::make_unique<QgsPieDiagram>();
662 }
663 else if ( diagramType == QgsTextDiagram::DIAGRAM_NAME_TEXT )
664 {
665 mDiagram = std::make_unique<QgsTextDiagram>();
666 }
667 else if ( diagramType == QgsHistogramDiagram::DIAGRAM_NAME_HISTOGRAM )
668 {
669 mDiagram = std::make_unique<QgsHistogramDiagram>();
670 }
671 else if ( diagramType == QgsStackedBarDiagram::DIAGRAM_NAME_STACKED_BAR )
672 {
673 mDiagram = std::make_unique<QgsStackedBarDiagram>();
674 }
675 else if ( diagramType == QgsStackedDiagram::DIAGRAM_NAME_STACKED )
676 {
677 mDiagram = std::make_unique<QgsStackedDiagram>();
678 }
679 else
680 {
681 // unknown diagram type -- default to histograms
682 mDiagram = std::make_unique<QgsHistogramDiagram>();
683 }
684 mShowAttributeLegend = ( elem.attribute( u"attributeLegend"_s, u"1"_s ) != "0"_L1 );
685}
686
687void QgsDiagramRenderer::_writeXml( QDomElement &rendererElem, QDomDocument &doc, const QgsReadWriteContext &context ) const
688{
689 Q_UNUSED( doc )
690 Q_UNUSED( context )
691
692 if ( mDiagram )
693 {
694 rendererElem.setAttribute( u"diagramType"_s, mDiagram->diagramName() );
695 }
696 rendererElem.setAttribute( u"attributeLegend"_s, mShowAttributeLegend );
697}
698
700
705
707{
708 Q_UNUSED( c )
709 s = mSettings;
710 return true;
711}
712
714{
715 return mDiagram->diagramSize( feature.attributes(), c, mSettings );
716}
717
719{
720 QList<QgsDiagramSettings> settingsList;
721 settingsList.push_back( mSettings );
722 return settingsList;
723}
724
725void QgsSingleCategoryDiagramRenderer::readXml( const QDomElement &elem, const QgsReadWriteContext &context )
726{
727 const QDomElement categoryElem = elem.firstChildElement( u"DiagramCategory"_s );
728 if ( categoryElem.isNull() )
729 {
730 return;
731 }
732
733 mSettings.readXml( categoryElem, context );
734 _readXml( elem, context );
735}
736
737void QgsSingleCategoryDiagramRenderer::writeXml( QDomElement &layerElem, QDomDocument &doc, const QgsReadWriteContext &context ) const
738{
739 QDomElement rendererElem = doc.createElement( u"SingleCategoryDiagramRenderer"_s );
740 mSettings.writeXml( rendererElem, doc, context );
741 _writeXml( rendererElem, doc, context );
742 layerElem.appendChild( rendererElem );
743}
744
746
748{
749 mInterpolationSettings.classificationAttributeIsExpression = false;
750}
751
753 : QgsDiagramRenderer( other )
754 , mSettings( other.mSettings )
755 , mInterpolationSettings( other.mInterpolationSettings )
756 , mDataDefinedSizeLegend( other.mDataDefinedSizeLegend ? new QgsDataDefinedSizeLegend( *other.mDataDefinedSizeLegend ) : nullptr )
757{}
758
761
763{
764 if ( &other == this )
765 {
766 return *this;
767 }
768 mSettings = other.mSettings;
769 mInterpolationSettings = other.mInterpolationSettings;
770 mDataDefinedSizeLegend = std::make_unique<QgsDataDefinedSizeLegend>( *other.mDataDefinedSizeLegend );
771
772 return *this;
773}
774
779
781{
782 QList<QgsDiagramSettings> settingsList;
783 settingsList.push_back( mSettings );
784 return settingsList;
785}
786
788{
789 s = mSettings;
790 s.size = diagramSize( feature, c );
791 return true;
792}
793
795{
796 return mSettings.categoryAttributes;
797}
798
800{
801 QSet< QString > referenced = QgsDiagramRenderer::referencedFields( context );
802 if ( mInterpolationSettings.classificationAttributeIsExpression )
803 {
804 QgsExpression *expression = mDiagram->getExpression( mInterpolationSettings.classificationAttributeExpression, context );
805 const auto constReferencedColumns = expression->referencedColumns();
806 for ( const QString &field : constReferencedColumns )
807 {
808 referenced << field;
809 }
810 }
811 else
812 {
813 referenced << mInterpolationSettings.classificationField;
814 }
815 return referenced;
816}
817
819{
820 return mDiagram->diagramSize( feature, c, mSettings, mInterpolationSettings );
821}
822
823void QgsLinearlyInterpolatedDiagramRenderer::readXml( const QDomElement &elem, const QgsReadWriteContext &context )
824{
825 mInterpolationSettings.lowerValue = elem.attribute( u"lowerValue"_s ).toDouble();
826 mInterpolationSettings.upperValue = elem.attribute( u"upperValue"_s ).toDouble();
827 mInterpolationSettings.lowerSize.setWidth( elem.attribute( u"lowerWidth"_s ).toDouble() );
828 mInterpolationSettings.lowerSize.setHeight( elem.attribute( u"lowerHeight"_s ).toDouble() );
829 mInterpolationSettings.upperSize.setWidth( elem.attribute( u"upperWidth"_s ).toDouble() );
830 mInterpolationSettings.upperSize.setHeight( elem.attribute( u"upperHeight"_s ).toDouble() );
831 mInterpolationSettings.classificationAttributeIsExpression = elem.hasAttribute( u"classificationAttributeExpression"_s );
832 if ( mInterpolationSettings.classificationAttributeIsExpression )
833 {
834 mInterpolationSettings.classificationAttributeExpression = elem.attribute( u"classificationAttributeExpression"_s );
835 }
836 else
837 {
838 mInterpolationSettings.classificationField = elem.attribute( u"classificationField"_s );
839 }
840 const QDomElement settingsElem = elem.firstChildElement( u"DiagramCategory"_s );
841 if ( !settingsElem.isNull() )
842 {
843 mSettings.readXml( settingsElem );
844 }
845
846 mDataDefinedSizeLegend.reset();
847
848 const QDomElement ddsLegendSizeElem = elem.firstChildElement( u"data-defined-size-legend"_s );
849 if ( !ddsLegendSizeElem.isNull() )
850 {
851 mDataDefinedSizeLegend.reset( QgsDataDefinedSizeLegend::readXml( ddsLegendSizeElem, context ) );
852 }
853 else
854 {
855 // pre-3.0 projects
856 if ( elem.attribute( u"sizeLegend"_s, u"0"_s ) != "0"_L1 )
857 {
858 mDataDefinedSizeLegend = std::make_unique<QgsDataDefinedSizeLegend>();
859 const QDomElement sizeLegendSymbolElem = elem.firstChildElement( u"symbol"_s );
860 if ( !sizeLegendSymbolElem.isNull() && sizeLegendSymbolElem.attribute( u"name"_s ) == "sizeSymbol"_L1 )
861 {
862 mDataDefinedSizeLegend->setSymbol( QgsSymbolLayerUtils::loadSymbol<QgsMarkerSymbol>( sizeLegendSymbolElem, context ).release() );
863 }
864 }
865 else
866 {
867 mDataDefinedSizeLegend = nullptr;
868 }
869 }
870
871 _readXml( elem, context );
872}
873
874void QgsLinearlyInterpolatedDiagramRenderer::writeXml( QDomElement &layerElem, QDomDocument &doc, const QgsReadWriteContext &context ) const
875{
876 QDomElement rendererElem = doc.createElement( u"LinearlyInterpolatedDiagramRenderer"_s );
877 rendererElem.setAttribute( u"lowerValue"_s, QString::number( mInterpolationSettings.lowerValue ) );
878 rendererElem.setAttribute( u"upperValue"_s, QString::number( mInterpolationSettings.upperValue ) );
879 rendererElem.setAttribute( u"lowerWidth"_s, QString::number( mInterpolationSettings.lowerSize.width() ) );
880 rendererElem.setAttribute( u"lowerHeight"_s, QString::number( mInterpolationSettings.lowerSize.height() ) );
881 rendererElem.setAttribute( u"upperWidth"_s, QString::number( mInterpolationSettings.upperSize.width() ) );
882 rendererElem.setAttribute( u"upperHeight"_s, QString::number( mInterpolationSettings.upperSize.height() ) );
883 if ( mInterpolationSettings.classificationAttributeIsExpression )
884 {
885 rendererElem.setAttribute( u"classificationAttributeExpression"_s, mInterpolationSettings.classificationAttributeExpression );
886 }
887 else
888 {
889 rendererElem.setAttribute( u"classificationField"_s, mInterpolationSettings.classificationField );
890 }
891 mSettings.writeXml( rendererElem, doc );
892
893 if ( mDataDefinedSizeLegend )
894 {
895 QDomElement ddsLegendElem = doc.createElement( u"data-defined-size-legend"_s );
896 mDataDefinedSizeLegend->writeXml( ddsLegendElem, context );
897 rendererElem.appendChild( ddsLegendElem );
898 }
899
900 _writeXml( rendererElem, doc, context );
901 layerElem.appendChild( rendererElem );
902}
903
905
907 : QgsDiagramRenderer( other )
908 , mSettings( other.mSettings )
909 , mDiagramRenderers()
910{
911 for ( QgsDiagramRenderer *renderer : std::as_const( other.mDiagramRenderers ) )
912 {
913 if ( renderer )
914 mDiagramRenderers << renderer->clone();
915 }
916}
917
919{
920 if ( &other == this )
921 return *this;
922
923 mSettings = other.mSettings;
924 qDeleteAll( mDiagramRenderers );
925 mDiagramRenderers.clear();
926 for ( QgsDiagramRenderer *renderer : std::as_const( other.mDiagramRenderers ) )
927 {
928 if ( renderer )
929 mDiagramRenderers << renderer->clone();
930 }
931
932 return *this;
933}
934
936{
937 qDeleteAll( mDiagramRenderers );
938}
939
944
946{
947 QSizeF stackedSize( 0, 0 );
948 int enabledDiagramCount = 0; // We'll add spacing only for enabled subDiagrams
949
950 // Iterate renderers. For each renderer, get the diagram
951 // size for the feature and add it to the total size
952 // accounting for stacked diagram defined spacing
953 for ( const QgsDiagramRenderer *subRenderer : std::as_const( mDiagramRenderers ) )
954 {
955 QSizeF size = subRenderer->sizeMapUnits( feature, c );
956
957 if ( size.isValid() )
958 {
959 enabledDiagramCount++;
960 switch ( mSettings.stackedDiagramMode )
961 {
963 stackedSize.setWidth( stackedSize.width() + size.width() );
964 stackedSize.setHeight( std::max( stackedSize.height(), size.height() ) );
965 break;
966
968 stackedSize.setWidth( std::max( stackedSize.width(), size.width() ) );
969 stackedSize.setHeight( stackedSize.height() + size.height() );
970 break;
971 }
972 }
973 }
974
975 if ( stackedSize.isValid() )
976 {
977 const double spacing = c.convertToMapUnits( mSettings.stackedDiagramSpacing(), mSettings.stackedDiagramSpacingUnit(), mSettings.stackedDiagramSpacingMapUnitScale() );
978
979 switch ( mSettings.stackedDiagramMode )
980 {
982 stackedSize.scale( stackedSize.width() + spacing * ( enabledDiagramCount - 1 ), stackedSize.height(), Qt::IgnoreAspectRatio );
983 break;
984
986 stackedSize.scale( stackedSize.width(), stackedSize.height() + spacing * ( enabledDiagramCount - 1 ), Qt::IgnoreAspectRatio );
987 break;
988 }
989 }
990 return stackedSize;
991}
992
993void QgsStackedDiagramRenderer::renderDiagram( const QgsFeature &feature, QgsRenderContext &c, QPointF pos, const QgsPropertyCollection &properties ) const
994{
995 if ( !mDiagram )
996 {
997 return;
998 }
999
1000 QPointF newPos = pos; // Each subdiagram will have its own newPos
1001
1002 // Get subrenderers sorted by mode (vertical diagrams are returned backwards)
1003 const QList< QgsDiagramRenderer * > stackedRenderers = renderers( true );
1004
1005 for ( const QgsDiagramRenderer *stackedRenderer : stackedRenderers )
1006 {
1007 if ( stackedRenderer->rendererName() == QgsStackedDiagramRenderer::DIAGRAM_RENDERER_NAME_STACKED )
1008 {
1009 // Nested stacked diagrams will use this recursion
1010 stackedRenderer->renderDiagram( feature, c, newPos, properties );
1011 continue;
1012 }
1013
1015 if ( !stackedRenderer->diagramSettings( feature, c, s ) )
1016 {
1017 continue;
1018 }
1019
1020 if ( !s.enabled )
1021 {
1022 continue;
1023 }
1024
1025 if ( s.scaleBasedVisibility )
1026 {
1027 // Note: scale might be a non-round number, so compare with qgsDoubleNear
1028 const double rendererScale = c.rendererScale();
1029
1030 // maxScale is inclusive ( < --> no diagram )
1031 double maxScale = s.maximumScale;
1032 if ( maxScale > 0 && QgsScaleUtils::lessThanMaximumScale( rendererScale, maxScale ) )
1033 {
1034 continue;
1035 }
1036
1037 // minScale is exclusive ( >= --> no diagram)
1038 double minScale = s.minimumScale;
1039 if ( minScale > 0 && QgsScaleUtils::equalToOrGreaterThanMinimumScale( rendererScale, minScale ) )
1040 {
1041 continue;
1042 }
1043 }
1044
1045 if ( properties.hasActiveProperties() )
1046 {
1047 c.expressionContext().setOriginalValueVariable( QgsColorUtils::colorToString( s.backgroundColor ) );
1049 c.expressionContext().setOriginalValueVariable( QgsColorUtils::colorToString( s.penColor ) );
1050 s.penColor = properties.valueAsColor( QgsDiagramLayerSettings::Property::StrokeColor, c.expressionContext(), s.penColor );
1051 c.expressionContext().setOriginalValueVariable( s.penWidth );
1052 s.penWidth = properties.valueAsDouble( QgsDiagramLayerSettings::Property::StrokeWidth, c.expressionContext(), s.penWidth );
1053 c.expressionContext().setOriginalValueVariable( s.rotationOffset );
1055 }
1056
1057 QgsPaintEffect *effect = s.paintEffect();
1058 std::unique_ptr< QgsEffectPainter > effectPainter;
1059 if ( effect && effect->enabled() )
1060 {
1061 effectPainter = std::make_unique< QgsEffectPainter >( c, effect );
1062 }
1063
1064 stackedRenderer->diagram()->renderDiagram( feature, c, s, newPos );
1065 QgsStackedDiagram *stackedDiagram = dynamic_cast< QgsStackedDiagram *>( mDiagram.get() );
1066 stackedDiagram->subDiagramPosition( newPos, c, mSettings, s );
1067 }
1068}
1069
1071{
1072 Q_UNUSED( feature )
1073 Q_UNUSED( c )
1074 Q_UNUSED( s )
1075 return false;
1076}
1077
1079{
1080 Q_UNUSED( feature )
1081 Q_UNUSED( c )
1082 return QSizeF( 0, 0 );
1083}
1084
1085QList<QgsDiagramSettings> QgsStackedDiagramRenderer::diagramSettings() const
1086{
1087 QList<QgsDiagramSettings> settingsList;
1088 settingsList.push_back( mSettings );
1089 return settingsList;
1090}
1091
1093{
1094 return mSettings.categoryAttributes;
1095}
1096
1097QList< QgsLayerTreeModelLegendNode * > QgsStackedDiagramRenderer::legendItems( QgsLayerTreeLayer *nodeLayer ) const
1098{
1099 QList< QgsLayerTreeModelLegendNode * > nodes;
1100 for ( const QgsDiagramRenderer *renderer : std::as_const( mDiagramRenderers ) )
1101 {
1102 nodes << renderer->legendItems( nodeLayer );
1103 }
1104
1105 return nodes;
1106}
1107
1108QList< QgsDiagramRenderer * > QgsStackedDiagramRenderer::renderers( bool sortByDiagramMode ) const
1109{
1110 QList< QgsDiagramRenderer * > renderers = mDiagramRenderers;
1111
1112 if ( sortByDiagramMode && mSettings.stackedDiagramMode == QgsDiagramSettings::Vertical )
1113 {
1114 // We draw vertical diagrams backwards, so
1115 // we return the subrenderers in reverse order
1116 std::reverse( renderers.begin(), renderers.end() );
1117 }
1118 return renderers;
1119}
1120
1122{
1123 if ( renderer )
1124 {
1125 mDiagramRenderers.append( renderer );
1126 }
1127}
1128
1130{
1131 return mDiagramRenderers.value( index );
1132}
1133
1135{
1136 return mDiagramRenderers.size();
1137}
1138
1139void QgsStackedDiagramRenderer::readXml( const QDomElement &elem, const QgsReadWriteContext &context )
1140{
1141 const QDomElement categoryElem = elem.firstChildElement( u"DiagramCategory"_s );
1142 if ( categoryElem.isNull() )
1143 {
1144 return;
1145 }
1146
1147 mSettings.readXml( categoryElem, context );
1148 _readXml( elem, context );
1149 _readXmlSubRenderers( elem, context );
1150}
1151
1152void QgsStackedDiagramRenderer::_readXmlSubRenderers( const QDomElement &elem, const QgsReadWriteContext &context )
1153{
1154 qDeleteAll( mDiagramRenderers );
1155 mDiagramRenderers.clear();
1156
1157 const QDomElement subRenderersElem = elem.firstChildElement( u"DiagramRenderers"_s );
1158
1159 if ( !subRenderersElem.isNull() )
1160 {
1161 const QDomNodeList childRendererList = subRenderersElem.childNodes();
1162
1163 for ( int i = 0; i < childRendererList.size(); i++ )
1164 {
1165 const QDomElement subRendererElem = childRendererList.at( i ).toElement();
1166
1167 if ( subRendererElem.nodeName() == "SingleCategoryDiagramRenderer"_L1 )
1168 {
1169 auto singleCatDiagramRenderer = std::make_unique< QgsSingleCategoryDiagramRenderer >();
1170 singleCatDiagramRenderer->readXml( subRendererElem, context );
1171 addRenderer( singleCatDiagramRenderer.release() );
1172 }
1173 else if ( subRendererElem.nodeName() == "LinearlyInterpolatedDiagramRenderer"_L1 )
1174 {
1175 auto linearDiagramRenderer = std::make_unique< QgsLinearlyInterpolatedDiagramRenderer >();
1176 linearDiagramRenderer->readXml( subRendererElem, context );
1177 addRenderer( linearDiagramRenderer.release() );
1178 }
1179 else if ( subRendererElem.nodeName() == "StackedDiagramRenderer"_L1 )
1180 {
1181 auto stackedDiagramRenderer = std::make_unique< QgsStackedDiagramRenderer >();
1182 stackedDiagramRenderer->readXml( subRendererElem, context );
1183 addRenderer( stackedDiagramRenderer.release() );
1184 }
1185 }
1186 }
1187}
1188
1189void QgsStackedDiagramRenderer::writeXml( QDomElement &layerElem, QDomDocument &doc, const QgsReadWriteContext &context ) const
1190{
1191 QDomElement rendererElem = doc.createElement( u"StackedDiagramRenderer"_s );
1192 mSettings.writeXml( rendererElem, doc, context );
1193 _writeXml( rendererElem, doc, context );
1194 _writeXmlSubRenderers( rendererElem, doc, context );
1195 layerElem.appendChild( rendererElem );
1196}
1197
1198void QgsStackedDiagramRenderer::_writeXmlSubRenderers( QDomElement &rendererElem, QDomDocument &doc, const QgsReadWriteContext &context ) const
1199{
1200 QDomElement renderersElem = doc.createElement( u"DiagramRenderers"_s );
1201
1202 // Iterate sub renderers and write their settings to a DOM object
1203 for ( int i = 0; i < mDiagramRenderers.count(); i++ )
1204 {
1205 mDiagramRenderers.at( i )->writeXml( renderersElem, doc, context );
1206 }
1207 rendererElem.appendChild( renderersElem );
1208}
1209
1210QList< QgsLayerTreeModelLegendNode * > QgsDiagramSettings::legendItems( QgsLayerTreeLayer *nodeLayer ) const
1211{
1212 QList< QgsLayerTreeModelLegendNode * > list;
1213 list.reserve( categoryLabels.size() );
1214 for ( int i = 0; i < categoryLabels.size(); ++i )
1215 {
1216 QPixmap pix( 16, 16 );
1217 pix.fill( categoryColors[i] );
1218 list << new QgsSimpleLegendNode( nodeLayer, categoryLabels[i], QIcon( pix ), nullptr, u"diagram_%1"_s.arg( QString::number( i ) ) );
1219 }
1220 return list;
1221}
1222
1224{
1225 return mAxisLineSymbol.get();
1226}
1227
1229{
1230 if ( axisLineSymbol != mAxisLineSymbol.get() )
1231 mAxisLineSymbol.reset( axisLineSymbol );
1232}
1233
1235{
1236 return mShowAxis;
1237}
1238
1240{
1241 mShowAxis = showAxis;
1242}
1243
1245{
1246 return mPaintEffect.get();
1247}
1248
1250{
1251 if ( effect != mPaintEffect.get() )
1252 mPaintEffect.reset( effect );
1253}
1254
1256 : mAxisLineSymbol( std::make_unique< QgsLineSymbol >() )
1257{}
1258
1260
1262 : enabled( other.enabled )
1263 , font( other.font )
1267 , size( other.size )
1268 , sizeType( other.sizeType )
1269 , sizeScale( other.sizeScale )
1270 , lineSizeUnit( other.lineSizeUnit )
1271 , lineSizeScale( other.lineSizeScale )
1273 , penColor( other.penColor )
1274 , penWidth( other.penWidth )
1278 , barWidth( other.barWidth )
1279 , opacity( other.opacity )
1280 , scaleByArea( other.scaleByArea )
1283 , maximumScale( other.maximumScale )
1284 , minimumScale( other.minimumScale )
1285 , minimumSize( other.minimumSize )
1286 , mSpacing( other.mSpacing )
1287 , mSpacingUnit( other.mSpacingUnit )
1288 , mSpacingMapUnitScale( other.mSpacingMapUnitScale )
1289 , mStackedDiagramSpacing( other.mStackedDiagramSpacing )
1290 , mStackedDiagramSpacingUnit( other.mStackedDiagramSpacingUnit )
1291 , mStackedDiagramSpacingMapUnitScale( other.mStackedDiagramSpacingMapUnitScale )
1292 , mDirection( other.mDirection )
1293 , mShowAxis( other.mShowAxis )
1294 , mAxisLineSymbol( other.mAxisLineSymbol ? other.mAxisLineSymbol->clone() : nullptr )
1295 , mPaintEffect( other.mPaintEffect ? other.mPaintEffect->clone() : nullptr )
1296{}
1297
1299{
1300 if ( &other == this )
1301 return *this;
1302
1303 enabled = other.enabled;
1304 font = other.font;
1308 size = other.size;
1309 sizeType = other.sizeType;
1310 sizeScale = other.sizeScale;
1311 lineSizeUnit = other.lineSizeUnit;
1314 penColor = other.penColor;
1315 penWidth = other.penWidth;
1319 barWidth = other.barWidth;
1320 opacity = other.opacity;
1321 scaleByArea = other.scaleByArea;
1324 maximumScale = other.maximumScale;
1325 minimumScale = other.minimumScale;
1326 minimumSize = other.minimumSize;
1327 mSpacing = other.mSpacing;
1328 mSpacingUnit = other.mSpacingUnit;
1329 mSpacingMapUnitScale = other.mSpacingMapUnitScale;
1330 mStackedDiagramSpacing = other.mStackedDiagramSpacing;
1331 mStackedDiagramSpacingUnit = other.mStackedDiagramSpacingUnit;
1332 mStackedDiagramSpacingMapUnitScale = other.mStackedDiagramSpacingMapUnitScale;
1333 mDirection = other.mDirection;
1334 mAxisLineSymbol.reset( other.mAxisLineSymbol ? other.mAxisLineSymbol->clone() : nullptr );
1335 mShowAxis = other.mShowAxis;
1336 mPaintEffect.reset( other.mPaintEffect ? other.mPaintEffect->clone() : nullptr );
1337 return *this;
1338}
1339
1341{
1342 return mDirection;
1343}
1344
1346{
1347 mDirection = direction;
1348}
1349
1350QList< QgsLayerTreeModelLegendNode * > QgsDiagramRenderer::legendItems( QgsLayerTreeLayer * ) const
1351{
1352 return QList< QgsLayerTreeModelLegendNode * >();
1353}
1354
1355QList< QgsLayerTreeModelLegendNode * > QgsSingleCategoryDiagramRenderer::legendItems( QgsLayerTreeLayer *nodeLayer ) const
1356{
1357 QList< QgsLayerTreeModelLegendNode * > nodes;
1358 if ( mShowAttributeLegend && mSettings.enabled )
1359 nodes = mSettings.legendItems( nodeLayer );
1360
1361 return nodes;
1362}
1363
1364QList< QgsLayerTreeModelLegendNode * > QgsLinearlyInterpolatedDiagramRenderer::legendItems( QgsLayerTreeLayer *nodeLayer ) const
1365{
1366 QList< QgsLayerTreeModelLegendNode * > nodes;
1367 if ( !mSettings.enabled )
1368 {
1369 return nodes;
1370 }
1371
1373 nodes = mSettings.legendItems( nodeLayer );
1374
1375 if ( mDataDefinedSizeLegend && mDiagram )
1376 {
1377 // add size legend
1378 QgsMarkerSymbol *legendSymbol = mDataDefinedSizeLegend->symbol() ? mDataDefinedSizeLegend->symbol()->clone() : QgsMarkerSymbol::createSimple( QVariantMap() ).release();
1379 legendSymbol->setSizeUnit( mSettings.sizeType );
1380 legendSymbol->setSizeMapUnitScale( mSettings.sizeScale );
1381
1382 QgsDataDefinedSizeLegend ddSizeLegend( *mDataDefinedSizeLegend );
1383 ddSizeLegend.setSymbol( legendSymbol ); // transfers ownership
1384
1385 QList<QgsDataDefinedSizeLegend::SizeClass> sizeClasses;
1386 if ( ddSizeLegend.classes().isEmpty() )
1387 {
1388 // automatic class creation if the classes are not defined manually
1389 const auto prettyBreaks { QgsSymbolLayerUtils::prettyBreaks( mInterpolationSettings.lowerValue, mInterpolationSettings.upperValue, 4 ) };
1390 for ( const double v : prettyBreaks )
1391 {
1392 const double size = mDiagram->legendSize( v, mSettings, mInterpolationSettings );
1393 sizeClasses << QgsDataDefinedSizeLegend::SizeClass( size, QString::number( v ) );
1394 }
1395 }
1396 else
1397 {
1398 // manual classes need to get size scaled because the QgsSizeScaleTransformer is not used in diagrams :-(
1399 const auto constClasses = ddSizeLegend.classes();
1400 for ( const QgsDataDefinedSizeLegend::SizeClass &sc : constClasses )
1401 {
1402 const double size = mDiagram->legendSize( sc.size, mSettings, mInterpolationSettings );
1403 sizeClasses << QgsDataDefinedSizeLegend::SizeClass( size, sc.label );
1404 }
1405 }
1406 ddSizeLegend.setClasses( sizeClasses );
1407
1408 const auto constLegendSymbolList = ddSizeLegend.legendSymbolList();
1409 for ( const QgsLegendSymbolItem &si : constLegendSymbolList )
1410 {
1411 if ( auto *lDataDefinedSizeLegendSettings = si.dataDefinedSizeLegendSettings() )
1412 nodes << new QgsDataDefinedSizeLegendNode( nodeLayer, *lDataDefinedSizeLegendSettings );
1413 else
1414 nodes << new QgsSymbolLegendNode( nodeLayer, si );
1415 }
1416 }
1417
1418 return nodes;
1419}
1420
1422{
1423 mDataDefinedSizeLegend.reset( settings );
1424}
1425
1427{
1428 return mDataDefinedSizeLegend.get();
1429}
@ MapUnits
Map units.
Definition qgis.h:5342
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.
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.
static QgsPaintEffectRegistry * paintEffectRegistry()
Returns the application's paint effect registry, used for managing paint effects.
static QString colorToString(const QColor &color)
Encodes a color into a string value.
Handles coordinate transforms between two coordinate systems.
A legend node with a marker symbol.
Object that keeps configuration of appearance of marker symbol's data-defined size in legend.
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.
void setSymbol(QgsMarkerSymbol *symbol SIP_TRANSFER)
Sets marker symbol that will be used to draw markers in legend.
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.
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...
QgsLegendSymbolList legendSymbolList() const
Generates legend symbol items according to the configuration.
LinePlacementFlag
Line placement flags for controlling line based placements.
QgsDiagramLayerSettings & operator=(const QgsDiagramLayerSettings &rh)
void setRenderer(QgsDiagramRenderer *diagramRenderer)
Sets the diagram renderer associated with the layer.
@ PositionX
X-coordinate data defined diagram position.
@ Distance
Distance to diagram from feature.
@ PositionY
Y-coordinate data defined diagram position.
@ Show
Whether to show the diagram.
@ Priority
Diagram priority (between 0 and 10).
@ ZIndex
Z-index for diagram ordering.
@ BackgroundColor
Diagram background color.
@ StartAngle
Angle offset for pie diagram.
@ IsObstacle
Whether diagram features act as obstacles for other diagrams/labels.
@ AlwaysShow
Whether the diagram should always be shown, even if it overlaps other diagrams/labels.
void readXml(const QDomElement &elem)
Reads the diagram settings from a DOM element.
static const QgsPropertiesDefinition & propertyDefinitions()
Returns the diagram property definitions.
QSet< QString > referencedFields(const QgsExpressionContext &context=QgsExpressionContext()) const
Returns the set of any fields referenced by the layer's diagrams.
void writeXml(QDomElement &layerElem, QDomDocument &doc) const
Writes the diagram settings to a DOM element.
bool prepare(const QgsExpressionContext &context=QgsExpressionContext()) const
Prepares the diagrams for a specified expression context.
void setCoordinateTransform(const QgsCoordinateTransform &transform)
Sets the coordinate transform associated with the layer.
Evaluates and returns the diagram settings relating to a diagram for a specific feature.
virtual QList< QgsLayerTreeModelLegendNode * > legendItems(QgsLayerTreeLayer *nodeLayer) const
Returns list of legend nodes for the diagram.
virtual QSizeF diagramSize(const QgsFeature &feature, const QgsRenderContext &c) const =0
Returns size of the diagram (in painter units) or an invalid size in case of error.
void _writeXml(QDomElement &rendererElem, QDomDocument &doc, const QgsReadWriteContext &context) const
Writes internal QgsDiagramRenderer diagram state to a DOM element.
virtual QSet< QString > referencedFields(const QgsExpressionContext &context=QgsExpressionContext()) const
Returns the set of any fields required for diagram rendering.
virtual 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.
void _readXml(const QDomElement &elem, const QgsReadWriteContext &context)
Reads internal QgsDiagramRenderer state from a DOM element.
QgsDiagramRenderer & operator=(const QgsDiagramRenderer &other)
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.
virtual QList< QString > diagramAttributes() const =0
Returns attribute indices needed for diagram rendering.
std::unique_ptr< QgsDiagram > mDiagram
Reference to the object that does the real diagram rendering.
void convertSizeToMapUnits(QSizeF &size, const QgsRenderContext &context) const
Converts size from mm to map units.
virtual QList< QgsDiagramSettings > diagramSettings() const =0
Returns list with all diagram settings in the renderer.
bool mShowAttributeLegend
Whether to show an attribute legend for the diagrams.
QgsDiagramRenderer()=default
virtual void readXml(const QDomElement &elem, const QgsReadWriteContext &context)=0
Reads diagram state from a DOM element.
void setDiagram(QgsDiagram *d)
virtual QgsDiagramRenderer * clone() const =0
Returns new instance that is equivalent to this one.
static int dpiPaintDevice(const QPainter *)
Returns the paint device dpi (or -1 in case of error.
Stores the settings for rendering a single diagram.
Direction direction() const
Returns the chart's angular direction.
StackedDiagramMode stackedDiagramMode
void setDirection(Direction direction)
Sets the chart's angular direction.
bool showAxis() const
Returns true if the diagram axis should be shown.
LabelPlacementMethod labelPlacementMethod
void readXml(const QDomElement &elem, const QgsReadWriteContext &context=QgsReadWriteContext())
Reads diagram settings from XML.
double opacity
Opacity, from 0 (transparent) to 1.0 (opaque).
QgsDiagramSettings & operator=(const QgsDiagramSettings &other)
QgsLineSymbol * axisLineSymbol() const
Returns the line symbol to use for rendering axis in diagrams.
Qgis::RenderUnit sizeType
Diagram size unit.
QList< QString > categoryAttributes
QList< QString > categoryLabels
DiagramOrientation diagramOrientation
QList< QgsLayerTreeModelLegendNode * > legendItems(QgsLayerTreeLayer *nodeLayer) const
Returns list of legend nodes for the diagram.
QgsMapUnitScale lineSizeScale
Line unit scale.
void writeXml(QDomElement &rendererElem, QDomDocument &doc, const QgsReadWriteContext &context=QgsReadWriteContext()) const
Writes diagram settings to XML.
QList< QColor > categoryColors
double rotationOffset
Rotation offset, in degrees clockwise from horizontal.
QgsMapUnitScale sizeScale
Diagram size unit scale.
double minimumScale
The minimum map scale (i.e.
double maximumScale
The maximum map scale (i.e.
void setAxisLineSymbol(QgsLineSymbol *symbol)
Sets the line symbol to use for rendering axis in diagrams.
Qgis::RenderUnit lineSizeUnit
Line unit index.
double minimumSize
Scale diagrams smaller than mMinimumSize to mMinimumSize.
Direction
Angular directions.
void setShowAxis(bool showAxis)
Sets whether the diagram axis should be shown.
void setPaintEffect(QgsPaintEffect *effect)
Sets the paint effect to use while rendering diagrams.
QgsPaintEffect * paintEffect() const
Returns the paint effect to use while rendering diagrams.
Base class for all diagram types.
Definition qgsdiagram.h:44
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
Handles parsing and evaluation of expressions (formerly called "search strings").
QSet< QString > referencedColumns() const
Gets list of columns referenced by the expression.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:60
QgsAttributes attributes
Definition qgsfeature.h:69
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.
static QDomElement toXmlElement(const QFont &font, QDomDocument &document, const QString &elementName)
Returns a DOM element containing the properties of the font.
static const QString DIAGRAM_NAME_HISTOGRAM
Layer tree node points to a map layer.
Stores information about one class/rule of a vector layer renderer in a unified way that can be used ...
A line symbol type, for rendering LineString and MultiLineString geometries.
void setDataDefinedSizeLegend(QgsDataDefinedSizeLegend *settings)
Configures appearance of legend.
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.
QList< QString > diagramAttributes() const override
Returns attribute indices needed for diagram rendering.
QSet< QString > referencedFields(const QgsExpressionContext &context=QgsExpressionContext()) const override
Returns the set of any fields required for diagram rendering.
QList< QgsDiagramSettings > diagramSettings() const override
Returns list with all diagram settings in the renderer.
void writeXml(QDomElement &layerElem, QDomDocument &doc, const QgsReadWriteContext &context) const override
Writes diagram state to a DOM element.
QgsLinearlyInterpolatedDiagramRenderer * clone() const override
Returns new instance that is equivalent to this one.
QList< QgsLayerTreeModelLegendNode * > legendItems(QgsLayerTreeLayer *nodeLayer) const override
Returns list of legend nodes for the diagram.
void readXml(const QDomElement &elem, const QgsReadWriteContext &context) override
Reads diagram state from a DOM element.
QgsDataDefinedSizeLegend * dataDefinedSizeLegend() const
Returns configuration of appearance of legend.
QgsLinearlyInterpolatedDiagramRenderer & operator=(const QgsLinearlyInterpolatedDiagramRenderer &other)
static const QString DIAGRAM_RENDERER_NAME_LINEARLY_INTERPOLATED
double mapUnitsPerPixel() const
Returns the current map units per pixel.
A marker symbol type, for rendering Point and MultiPoint geometries.
static std::unique_ptr< QgsMarkerSymbol > createSimple(const QVariantMap &properties)
Create a marker symbol with one symbol layer: SimpleMarker with specified properties.
QgsMarkerSymbol * clone() const override
Returns a deep copy of this symbol.
void setSizeMapUnitScale(const QgsMapUnitScale &scale) const
Sets the size map unit scale for the whole symbol (including all symbol layers).
void setSizeUnit(Qgis::RenderUnit unit) const
Sets the size units for the whole symbol (including all symbol layers).
static QgsPaintEffect * defaultStack()
Returns a new effect stack consisting of a sensible selection of default effects.
static bool isDefaultStack(QgsPaintEffect *effect)
Tests whether a paint effect matches the default effects stack.
Base class for visual effects which can be applied to QPicture drawings.
bool enabled() const
Returns whether the effect is enabled.
static const QString DIAGRAM_NAME_PIE
A grouped map of multiple QgsProperty objects, each referenced by an integer key value.
bool hasActiveProperties() const final
Returns true if the collection has any active properties, or false if all properties within the colle...
@ Double
Double value (including negative values).
Definition qgsproperty.h:56
@ StrokeWidth
Line stroke width.
Definition qgsproperty.h:71
@ Boolean
Boolean value.
Definition qgsproperty.h:52
@ Rotation
Rotation (value between 0-360 degrees).
Definition qgsproperty.h:59
@ ColorWithAlpha
Color with alpha channel.
Definition qgsproperty.h:63
@ DoublePositive
Positive double value (including 0).
Definition qgsproperty.h:57
A container for the context for various read/write operations on objects.
Contains information about the context of a rendering operation.
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
const QgsMapToPixel & mapToPixel() const
Returns the context's map to pixel transform, which transforms between map coordinates and device coo...
static bool equalToOrGreaterThanMinimumScale(const double scale, const double minScale)
Returns whether the scale is equal to or greater than the minScale, taking non-round numbers into acc...
static bool lessThanMaximumScale(const double scale, const double maxScale)
Returns whether the scale is less than the maxScale, taking non-round numbers into account.
Implementation of legend node interface for displaying arbitrary labels with icons.
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.
void writeXml(QDomElement &layerElem, QDomDocument &doc, const QgsReadWriteContext &context) const override
Writes diagram state to a DOM element.
QgsSingleCategoryDiagramRenderer * clone() const override
Returns new instance that is equivalent to this one.
QList< QgsLayerTreeModelLegendNode * > legendItems(QgsLayerTreeLayer *nodeLayer) const override
Returns list of legend nodes for the diagram.
void readXml(const QDomElement &elem, const QgsReadWriteContext &context) override
Reads diagram state from a DOM element.
QList< QgsDiagramSettings > diagramSettings() const override
Returns list with all diagram settings in the renderer.
static const QString DIAGRAM_RENDERER_NAME_SINGLE_CATEGORY
static const QString DIAGRAM_NAME_STACKED_BAR
QgsStackedDiagramRenderer * clone() const override
Returns new instance that is equivalent to this one.
QList< QgsDiagramSettings > diagramSettings() const override
Returns list with all diagram settings in the renderer.
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.
void renderDiagram(const QgsFeature &feature, QgsRenderContext &c, QPointF pos, const QgsPropertyCollection &properties=QgsPropertyCollection()) const override
Renders the diagram for a specified feature at a specific position in the passed render context,...
QList< QString > diagramAttributes() const override
Returns attribute indices needed for diagram rendering.
void writeXml(QDomElement &layerElem, QDomDocument &doc, const QgsReadWriteContext &context) const override
Writes diagram state to a DOM element.
static const QString DIAGRAM_RENDERER_NAME_STACKED
QgsStackedDiagramRenderer & operator=(const QgsStackedDiagramRenderer &other)
void readXml(const QDomElement &elem, const QgsReadWriteContext &context) override
Reads diagram state from a DOM element.
void addRenderer(QgsDiagramRenderer *renderer)
Adds a renderer to the stacked renderer object.
void _writeXmlSubRenderers(QDomElement &rendererElem, QDomDocument &doc, const QgsReadWriteContext &context) const
Writes stacked renderers state to a DOM element.
QList< QgsDiagramRenderer * > renderers(bool sortByDiagramMode=false) const
Returns an ordered list with the renderers of the stacked renderer object.
const QgsDiagramRenderer * renderer(const int index) const
Returns the renderer at the given index.
void _readXmlSubRenderers(const QDomElement &elem, const QgsReadWriteContext &context)
Reads stacked renderers state from a DOM element.
int rendererCount() const
Returns the number of sub renderers in the stacked diagram renderer.
QList< QgsLayerTreeModelLegendNode * > legendItems(QgsLayerTreeLayer *nodeLayer) const override
Returns list of legend nodes for the diagram.
QgsStackedDiagramRenderer()=default
QSizeF sizeMapUnits(const QgsFeature &feature, const QgsRenderContext &c) const override
Returns size of the diagram for a feature in map units. Returns an invalid QSizeF in case of error.
A diagram composed of several subdiagrams, located side by side.
static const QString DIAGRAM_NAME_STACKED
void subDiagramPosition(QPointF &newPos, const QgsRenderContext &c, const QgsDiagramSettings &s, const QgsDiagramSettings &subSettings)
Calculates the position for the next subdiagram, updating the newPos object.
static QString encodeMapUnitScale(const QgsMapUnitScale &mapUnitScale)
static QList< double > prettyBreaks(double minimum, double maximum, int classes)
Computes a sequence of about 'classes' equally spaced round values which cover the range of values fr...
static QgsMapUnitScale decodeMapUnitScale(const QString &str)
static std::unique_ptr< QgsSymbol > loadSymbol(const QDomElement &element, const QgsReadWriteContext &context)
Attempts to load a symbol from a DOM element.
static QDomElement saveSymbol(const QString &symbolName, const QgsSymbol *symbol, QDomDocument &doc, const QgsReadWriteContext &context)
Writes a symbol definition to XML.
Implementation of legend node interface for displaying preview of vector symbols and their labels and...
static const QString DIAGRAM_NAME_TEXT
static Q_INVOKABLE Qgis::RenderUnit decodeRenderUnit(const QString &string, bool *ok=nullptr)
Decodes a render unit from a string.
static Q_INVOKABLE QString encodeUnit(Qgis::DistanceUnit unit)
Encodes a distance unit to a string.
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
QMap< int, QgsPropertyDefinition > QgsPropertiesDefinition
Definition of available properties.
Definition of one class for the legend.