QGIS API Documentation 3.99.0-Master (d270888f95f)
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
54 {
55 { static_cast< int >( QgsDiagramLayerSettings::Property::BackgroundColor ), 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}
111
113{
114 if ( &rh == this )
115 return *this;
116
117 //****** IMPORTANT! editing this? make sure you update the move assignment operator too! *****
118 mPlacement = rh.mPlacement;
119 mPlacementFlags = rh.mPlacementFlags;
120 mPriority = rh.mPriority;
121 mZIndex = rh.mZIndex;
122 mObstacle = rh.mObstacle;
123 mDistance = rh.mDistance;
124 mRenderer.reset( rh.mRenderer ? rh.mRenderer->clone() : nullptr );
125 mCt = rh.mCt;
126 mShowAll = rh.mShowAll;
127 mDataDefinedProperties = rh.mDataDefinedProperties;
128 //****** IMPORTANT! editing this? make sure you update the move assignment operator too! *****
129 return *this;
130}
131
133{
134 if ( &rh == this )
135 return *this;
136
137 mPlacement = rh.mPlacement;
138 mPlacementFlags = rh.mPlacementFlags;
139 mPriority = rh.mPriority;
140 mZIndex = rh.mZIndex;
141 mObstacle = rh.mObstacle;
142 mDistance = rh.mDistance;
143 mRenderer = std::move( rh.mRenderer );
144 mCt = std::move( rh.mCt );
145 mShowAll = rh.mShowAll;
146 mDataDefinedProperties = std::move( rh.mDataDefinedProperties );
147 return *this;
148}
149
154
156{
157 if ( diagramRenderer == mRenderer.get() )
158 return;
159
160 mRenderer.reset( diagramRenderer );
161
162}
163
165{
166 mCt = transform;
167}
168
169void QgsDiagramLayerSettings::readXml( const QDomElement &elem )
170{
171 const QDomNodeList propertyElems = elem.elementsByTagName( u"properties"_s );
172 if ( !propertyElems.isEmpty() )
173 {
174 ( void )mDataDefinedProperties.readXml( propertyElems.at( 0 ).toElement(), sPropertyDefinitions );
175 }
176 else
177 {
178 mDataDefinedProperties.clear();
179 }
180
181 mPlacement = static_cast< Placement >( elem.attribute( u"placement"_s ).toInt() );
182 mPlacementFlags = static_cast< LinePlacementFlag >( elem.attribute( u"linePlacementFlags"_s ).toInt() );
183 mPriority = elem.attribute( u"priority"_s ).toInt();
184 mZIndex = elem.attribute( u"zIndex"_s ).toDouble();
185 mObstacle = elem.attribute( u"obstacle"_s ).toInt();
186 mDistance = elem.attribute( u"dist"_s ).toDouble();
187 mShowAll = ( elem.attribute( u"showAll"_s, u"0"_s ) != "0"_L1 );
188}
189
190void QgsDiagramLayerSettings::writeXml( QDomElement &layerElem, QDomDocument &doc ) const
191{
192 QDomElement diagramLayerElem = doc.createElement( u"DiagramLayerSettings"_s );
193 QDomElement propertiesElem = doc.createElement( u"properties"_s );
194 ( void )mDataDefinedProperties.writeXml( propertiesElem, sPropertyDefinitions );
195 diagramLayerElem.appendChild( propertiesElem );
196 diagramLayerElem.setAttribute( u"placement"_s, mPlacement );
197 diagramLayerElem.setAttribute( u"linePlacementFlags"_s, mPlacementFlags );
198 diagramLayerElem.setAttribute( u"priority"_s, mPriority );
199 diagramLayerElem.setAttribute( u"zIndex"_s, mZIndex );
200 diagramLayerElem.setAttribute( u"obstacle"_s, mObstacle );
201 diagramLayerElem.setAttribute( u"dist"_s, QString::number( mDistance ) );
202 diagramLayerElem.setAttribute( u"showAll"_s, mShowAll );
203 layerElem.appendChild( diagramLayerElem );
204}
205
207{
208 return mDataDefinedProperties.prepare( context );
209}
210
212{
213 QSet< QString > referenced;
214 if ( mRenderer )
215 referenced = mRenderer->referencedFields( context );
216
217 //add the ones needed for data defined settings
218 referenced.unite( mDataDefinedProperties.referencedFields( context ) );
219
220 return referenced;
221}
222
223void QgsDiagramSettings::readXml( const QDomElement &elem, const QgsReadWriteContext &context )
224{
225 enabled = ( elem.attribute( u"enabled"_s, u"1"_s ) != "0"_L1 );
226 if ( !QgsFontUtils::setFromXmlChildNode( font, elem, u"fontProperties"_s ) )
227 {
228 font.fromString( elem.attribute( u"font"_s ) );
229 }
230 backgroundColor.setNamedColor( elem.attribute( u"backgroundColor"_s ) );
231 backgroundColor.setAlpha( elem.attribute( u"backgroundAlpha"_s ).toInt() );
232 size.setWidth( elem.attribute( u"width"_s ).toDouble() );
233 size.setHeight( elem.attribute( u"height"_s ).toDouble() );
234 if ( elem.hasAttribute( u"transparency"_s ) )
235 {
236 opacity = 1 - elem.attribute( u"transparency"_s, u"0"_s ).toInt() / 255.0;
237 }
238 else
239 {
240 opacity = elem.attribute( u"opacity"_s, u"1.00"_s ).toDouble();
241 }
242
243 penColor.setNamedColor( elem.attribute( u"penColor"_s ) );
244 const int penAlpha = elem.attribute( u"penAlpha"_s, u"255"_s ).toInt();
245 penColor.setAlpha( penAlpha );
246 penWidth = elem.attribute( u"penWidth"_s ).toDouble();
247
248 mDirection = static_cast< Direction >( elem.attribute( u"direction"_s, u"1"_s ).toInt() );
249
250 maximumScale = elem.attribute( u"minScaleDenominator"_s, u"-1"_s ).toDouble();
251 minimumScale = elem.attribute( u"maxScaleDenominator"_s, u"-1"_s ).toDouble();
252 if ( elem.hasAttribute( u"scaleBasedVisibility"_s ) )
253 {
254 scaleBasedVisibility = ( elem.attribute( u"scaleBasedVisibility"_s, u"1"_s ) != "0"_L1 );
255 }
256 else
257 {
259 }
260
261 //diagram size unit type and scale
262 if ( elem.attribute( u"sizeType"_s ) == "MapUnits"_L1 )
263 {
264 //compatibility with pre-2.16 project files
266 }
267 else
268 {
269 sizeType = QgsUnitTypes::decodeRenderUnit( elem.attribute( u"sizeType"_s ) );
270 }
271 sizeScale = QgsSymbolLayerUtils::decodeMapUnitScale( elem.attribute( u"sizeScale"_s ) );
272
273 //line width unit type and scale
274 lineSizeUnit = QgsUnitTypes::decodeRenderUnit( elem.attribute( u"lineSizeType"_s ) );
275 lineSizeScale = QgsSymbolLayerUtils::decodeMapUnitScale( elem.attribute( u"lineSizeScale"_s ) );
276
277 mSpacing = elem.attribute( u"spacing"_s ).toDouble();
278 mSpacingUnit = QgsUnitTypes::decodeRenderUnit( elem.attribute( u"spacingUnit"_s ) );
279 mSpacingMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( elem.attribute( u"spacingUnitScale"_s ) );
280
281 mStackedDiagramSpacing = elem.attribute( u"stackedDiagramSpacing"_s ).toDouble();
282 mStackedDiagramSpacingUnit = QgsUnitTypes::decodeRenderUnit( elem.attribute( u"stackedDiagramSpacingUnit"_s ) );
283 mStackedDiagramSpacingMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( elem.attribute( u"stackedDiagramSpacingUnitScale"_s ) );
284
285 //label placement method
286 if ( elem.attribute( u"labelPlacementMethod"_s ) == "Height"_L1 )
287 {
289 }
290 else
291 {
293 }
294
295 // orientation
296 if ( elem.attribute( u"diagramOrientation"_s ) == "Left"_L1 )
297 {
299 }
300 else if ( elem.attribute( u"diagramOrientation"_s ) == "Right"_L1 )
301 {
303 }
304 else if ( elem.attribute( u"diagramOrientation"_s ) == "Down"_L1 )
305 {
307 }
308 else
309 {
311 }
312
313 // stacked mode
314 if ( elem.attribute( u"stackedDiagramMode"_s ) == "Horizontal"_L1 )
315 {
317 }
318 else if ( elem.attribute( u"stackedDiagramMode"_s ) == "Vertical"_L1 )
319 {
321 }
322
323 // scale dependency
324 if ( elem.attribute( u"scaleDependency"_s ) == "Diameter"_L1 )
325 {
326 scaleByArea = false;
327 }
328 else
329 {
330 scaleByArea = true;
331 }
332
333 barWidth = elem.attribute( u"barWidth"_s ).toDouble();
334
335 if ( elem.hasAttribute( u"angleOffset"_s ) )
336 rotationOffset = std::fmod( 360.0 - elem.attribute( u"angleOffset"_s ).toInt() / 16.0, 360.0 );
337 else
338 rotationOffset = elem.attribute( u"rotationOffset"_s ).toDouble();
339
340 minimumSize = elem.attribute( u"minimumSize"_s ).toDouble();
341
342 const QDomNodeList axisSymbolNodes = elem.elementsByTagName( u"axisSymbol"_s );
343 if ( axisSymbolNodes.count() > 0 )
344 {
345 const QDomElement axisSymbolElem = axisSymbolNodes.at( 0 ).toElement().firstChildElement();
346 mAxisLineSymbol = QgsSymbolLayerUtils::loadSymbol<QgsLineSymbol>( axisSymbolElem, context );
347 }
348 else
349 {
350 mAxisLineSymbol = std::make_unique< QgsLineSymbol >();
351 }
352
353 mShowAxis = elem.attribute( u"showAxis"_s, u"0"_s ).toInt();
354
355 //colors
356 categoryColors.clear();
357 const QDomNodeList attributes = elem.elementsByTagName( u"attribute"_s );
358
359
360 if ( attributes.length() > 0 )
361 {
362 for ( int i = 0; i < attributes.size(); i++ )
363 {
364 const QDomElement attrElem = attributes.at( i ).toElement();
365 QColor newColor( attrElem.attribute( u"color"_s ) );
366 newColor.setAlphaF( attrElem.attribute( u"colorOpacity"_s, u"1.0"_s ).toDouble() );
367 categoryColors.append( newColor );
368 categoryAttributes.append( attrElem.attribute( u"field"_s ) );
369 categoryLabels.append( attrElem.attribute( u"label"_s ) );
370 if ( categoryLabels.constLast().isEmpty() )
371 {
372 categoryLabels.back() = categoryAttributes.back();
373 }
374 }
375 }
376 else
377 {
378 // Restore old format attributes and colors
379
380 const QStringList colorList = elem.attribute( u"colors"_s ).split( '/' );
381 QStringList::const_iterator colorIt = colorList.constBegin();
382 for ( ; colorIt != colorList.constEnd(); ++colorIt )
383 {
384 QColor newColor( *colorIt );
385 categoryColors.append( QColor( newColor ) );
386 }
387
388 //attribute indices
389 categoryAttributes.clear();
390 const QStringList catList = elem.attribute( u"categories"_s ).split( '/' );
391 QStringList::const_iterator catIt = catList.constBegin();
392 for ( ; catIt != catList.constEnd(); ++catIt )
393 {
394 categoryAttributes.append( *catIt );
395 categoryLabels.append( *catIt );
396 }
397 }
398
399 const QDomElement effectElem = elem.firstChildElement( u"effect"_s );
400 if ( !effectElem.isNull() )
401 setPaintEffect( QgsApplication::paintEffectRegistry()->createEffect( effectElem ) );
402 else
404}
405
406void QgsDiagramSettings::writeXml( QDomElement &rendererElem, QDomDocument &doc, const QgsReadWriteContext &context ) const
407{
408 QDomElement categoryElem = doc.createElement( u"DiagramCategory"_s );
409 categoryElem.setAttribute( u"enabled"_s, enabled );
410 categoryElem.appendChild( QgsFontUtils::toXmlElement( font, doc, u"fontProperties"_s ) );
411 categoryElem.setAttribute( u"backgroundColor"_s, backgroundColor.name() );
412 categoryElem.setAttribute( u"backgroundAlpha"_s, backgroundColor.alpha() );
413 categoryElem.setAttribute( u"width"_s, QString::number( size.width() ) );
414 categoryElem.setAttribute( u"height"_s, QString::number( size.height() ) );
415 categoryElem.setAttribute( u"penColor"_s, penColor.name() );
416 categoryElem.setAttribute( u"penAlpha"_s, penColor.alpha() );
417 categoryElem.setAttribute( u"penWidth"_s, QString::number( penWidth ) );
418 categoryElem.setAttribute( u"scaleBasedVisibility"_s, scaleBasedVisibility );
419 categoryElem.setAttribute( u"minScaleDenominator"_s, QString::number( maximumScale ) );
420 categoryElem.setAttribute( u"maxScaleDenominator"_s, QString::number( minimumScale ) );
421 categoryElem.setAttribute( u"opacity"_s, QString::number( opacity ) );
422 categoryElem.setAttribute( u"spacing"_s, QString::number( mSpacing ) );
423 categoryElem.setAttribute( u"spacingUnit"_s, QgsUnitTypes::encodeUnit( mSpacingUnit ) );
424 categoryElem.setAttribute( u"spacingUnitScale"_s, QgsSymbolLayerUtils::encodeMapUnitScale( mSpacingMapUnitScale ) );
425 categoryElem.setAttribute( u"stackedDiagramSpacing"_s, QString::number( mStackedDiagramSpacing ) );
426 categoryElem.setAttribute( u"stackedDiagramSpacingUnit"_s, QgsUnitTypes::encodeUnit( mStackedDiagramSpacingUnit ) );
427 categoryElem.setAttribute( u"stackedDiagramSpacingUnitScale"_s, QgsSymbolLayerUtils::encodeMapUnitScale( mStackedDiagramSpacingMapUnitScale ) );
428 categoryElem.setAttribute( u"direction"_s, QString::number( mDirection ) );
429
430 //diagram size unit type and scale
431 categoryElem.setAttribute( u"sizeType"_s, QgsUnitTypes::encodeUnit( sizeType ) );
432 categoryElem.setAttribute( u"sizeScale"_s, QgsSymbolLayerUtils::encodeMapUnitScale( sizeScale ) );
433
434 //line width unit type and scale
435 categoryElem.setAttribute( u"lineSizeType"_s, QgsUnitTypes::encodeUnit( lineSizeUnit ) );
436 categoryElem.setAttribute( u"lineSizeScale"_s, QgsSymbolLayerUtils::encodeMapUnitScale( lineSizeScale ) );
437
438 // label placement method (text diagram)
440 {
441 categoryElem.setAttribute( u"labelPlacementMethod"_s, u"Height"_s );
442 }
443 else
444 {
445 categoryElem.setAttribute( u"labelPlacementMethod"_s, u"XHeight"_s );
446 }
447
448 if ( scaleByArea )
449 {
450 categoryElem.setAttribute( u"scaleDependency"_s, u"Area"_s );
451 }
452 else
453 {
454 categoryElem.setAttribute( u"scaleDependency"_s, u"Diameter"_s );
455 }
456
457 // orientation (histogram)
458 switch ( diagramOrientation )
459 {
460 case Left:
461 categoryElem.setAttribute( u"diagramOrientation"_s, u"Left"_s );
462 break;
463
464 case Right:
465 categoryElem.setAttribute( u"diagramOrientation"_s, u"Right"_s );
466 break;
467
468 case Down:
469 categoryElem.setAttribute( u"diagramOrientation"_s, u"Down"_s );
470 break;
471
472 case Up:
473 categoryElem.setAttribute( u"diagramOrientation"_s, u"Up"_s );
474 break;
475 }
476
477 // stacked mode
478 switch ( stackedDiagramMode )
479 {
480 case Horizontal:
481 categoryElem.setAttribute( u"stackedDiagramMode"_s, u"Horizontal"_s );
482 break;
483
484 case Vertical:
485 categoryElem.setAttribute( u"stackedDiagramMode"_s, u"Vertical"_s );
486 break;
487 }
488
489 categoryElem.setAttribute( u"barWidth"_s, QString::number( barWidth ) );
490 categoryElem.setAttribute( u"minimumSize"_s, QString::number( minimumSize ) );
491 categoryElem.setAttribute( u"rotationOffset"_s, QString::number( rotationOffset ) );
492
493 const int nCats = std::min( categoryColors.size(), categoryAttributes.size() );
494 for ( int i = 0; i < nCats; ++i )
495 {
496 QDomElement attributeElem = doc.createElement( u"attribute"_s );
497
498 attributeElem.setAttribute( u"field"_s, categoryAttributes.at( i ) );
499 attributeElem.setAttribute( u"color"_s, categoryColors.at( i ).name() );
500 attributeElem.setAttribute( u"colorOpacity"_s, QString::number( categoryColors.at( i ).alphaF() ) );
501 attributeElem.setAttribute( u"label"_s, categoryLabels.at( i ) );
502 categoryElem.appendChild( attributeElem );
503 }
504
505 categoryElem.setAttribute( u"showAxis"_s, mShowAxis ? u"1"_s : u"0"_s );
506 QDomElement axisSymbolElem = doc.createElement( u"axisSymbol"_s );
507 const QDomElement symbolElem = QgsSymbolLayerUtils::saveSymbol( QString(), mAxisLineSymbol.get(), doc, context );
508 axisSymbolElem.appendChild( symbolElem );
509 categoryElem.appendChild( axisSymbolElem );
510
511 if ( mPaintEffect && !QgsPaintEffectRegistry::isDefaultStack( mPaintEffect.get() ) )
512 mPaintEffect->saveProperties( doc, categoryElem );
513
514 rendererElem.appendChild( categoryElem );
515}
516
518{
519 if ( mDiagram.get() == d )
520 return;
521
522 mDiagram.reset( d );
523}
524
530
532{
533 if ( &other == this )
534 return *this;
535
536 mDiagram.reset( other.mDiagram ? other.mDiagram->clone() : nullptr );
538 return *this;
539}
540
541void QgsDiagramRenderer::renderDiagram( const QgsFeature &feature, QgsRenderContext &c, QPointF pos, const QgsPropertyCollection &properties ) const
542{
543 if ( !mDiagram )
544 {
545 return;
546 }
547
549 if ( !diagramSettings( feature, c, s ) )
550 {
551 return;
552 }
553
554 if ( properties.hasActiveProperties() )
555 {
556 c.expressionContext().setOriginalValueVariable( QgsColorUtils::colorToString( s.backgroundColor ) );
558 c.expressionContext().setOriginalValueVariable( QgsColorUtils::colorToString( s.penColor ) );
559 s.penColor = properties.valueAsColor( QgsDiagramLayerSettings::Property::StrokeColor, c.expressionContext(), s.penColor );
560 c.expressionContext().setOriginalValueVariable( s.penWidth );
561 s.penWidth = properties.valueAsDouble( QgsDiagramLayerSettings::Property::StrokeWidth, c.expressionContext(), s.penWidth );
562 c.expressionContext().setOriginalValueVariable( s.rotationOffset );
564 }
565
566 QgsPaintEffect *effect = s.paintEffect();
567 std::unique_ptr< QgsEffectPainter > effectPainter;
568 if ( effect && effect->enabled() )
569 {
570 effectPainter = std::make_unique< QgsEffectPainter >( c, effect );
571 }
572
573 mDiagram->renderDiagram( feature, c, s, pos );
574}
575
576QSizeF QgsDiagramRenderer::sizeMapUnits( const QgsFeature &feature, const QgsRenderContext &c ) const
577{
579 if ( !diagramSettings( feature, c, s ) || !s.enabled )
580 {
581 return QSizeF();
582 }
583
584 if ( s.scaleBasedVisibility )
585 {
586 // Note: scale might be a non-round number, so compare with qgsDoubleNear
587 const double rendererScale = c.rendererScale();
588
589 // maxScale is inclusive ( < --> no size )
590 double maxScale = s.maximumScale;
591 if ( maxScale > 0 && QgsScaleUtils::lessThanMaximumScale( rendererScale, maxScale ) )
592 {
593 return QSizeF();
594 }
595
596 // minScale is exclusive ( >= --> no size)
597 double minScale = s.minimumScale;
598 if ( minScale > 0 && QgsScaleUtils::equalToOrGreaterThanMinimumScale( rendererScale, minScale ) )
599 {
600 return QSizeF();
601 }
602 }
603
604 QSizeF size = diagramSize( feature, c );
605 if ( size.isValid() )
606 {
607 const double width = c.convertToMapUnits( size.width(), s.sizeType, s.sizeScale );
608 size.rheight() *= width / size.width();
609 size.setWidth( width );
610 }
611 return size;
612}
613
615{
616 QSet< QString > referenced;
617
618 if ( !mDiagram )
619 return referenced;
620
621 const auto constDiagramAttributes = diagramAttributes();
622 for ( const QString &att : constDiagramAttributes )
623 {
624 QgsExpression *expression = mDiagram->getExpression( att, context );
625 const auto constReferencedColumns = expression->referencedColumns();
626 for ( const QString &field : constReferencedColumns )
627 {
628 referenced << field;
629 }
630 }
631 return referenced;
632}
633
634void QgsDiagramRenderer::convertSizeToMapUnits( QSizeF &size, const QgsRenderContext &context ) const
635{
636 if ( !size.isValid() )
637 {
638 return;
639 }
640
641 const double pixelToMap = context.scaleFactor() * context.mapToPixel().mapUnitsPerPixel();
642 size.rwidth() *= pixelToMap;
643 size.rheight() *= pixelToMap;
644}
645
646int QgsDiagramRenderer::dpiPaintDevice( const QPainter *painter )
647{
648 if ( painter )
649 {
650 QPaintDevice *device = painter->device();
651 if ( device )
652 {
653 return device->logicalDpiX();
654 }
655 }
656 return -1;
657}
658
659void QgsDiagramRenderer::_readXml( const QDomElement &elem, const QgsReadWriteContext &context )
660{
661 Q_UNUSED( context )
662 mDiagram.reset();
663 const QString diagramType = elem.attribute( u"diagramType"_s );
664 if ( diagramType == QgsPieDiagram::DIAGRAM_NAME_PIE )
665 {
666 mDiagram = std::make_unique<QgsPieDiagram>( );
667 }
668 else if ( diagramType == QgsTextDiagram::DIAGRAM_NAME_TEXT )
669 {
670 mDiagram = std::make_unique<QgsTextDiagram>( );
671 }
672 else if ( diagramType == QgsHistogramDiagram::DIAGRAM_NAME_HISTOGRAM )
673 {
674 mDiagram = std::make_unique<QgsHistogramDiagram>( );
675 }
676 else if ( diagramType == QgsStackedBarDiagram::DIAGRAM_NAME_STACKED_BAR )
677 {
678 mDiagram = std::make_unique<QgsStackedBarDiagram>( );
679 }
680 else if ( diagramType == QgsStackedDiagram::DIAGRAM_NAME_STACKED )
681 {
682 mDiagram = std::make_unique<QgsStackedDiagram>( );
683 }
684 else
685 {
686 // unknown diagram type -- default to histograms
687 mDiagram = std::make_unique<QgsHistogramDiagram>( );
688 }
689 mShowAttributeLegend = ( elem.attribute( u"attributeLegend"_s, u"1"_s ) != "0"_L1 );
690}
691
692void QgsDiagramRenderer::_writeXml( QDomElement &rendererElem, QDomDocument &doc, const QgsReadWriteContext &context ) const
693{
694 Q_UNUSED( doc )
695 Q_UNUSED( context )
696
697 if ( mDiagram )
698 {
699 rendererElem.setAttribute( u"diagramType"_s, mDiagram->diagramName() );
700 }
701 rendererElem.setAttribute( u"attributeLegend"_s, mShowAttributeLegend );
702}
703
705
710
712{
713 Q_UNUSED( c )
714 s = mSettings;
715 return true;
716}
717
719{
720 return mDiagram->diagramSize( feature.attributes(), c, mSettings );
721}
722
724{
725 QList<QgsDiagramSettings> settingsList;
726 settingsList.push_back( mSettings );
727 return settingsList;
728}
729
730void QgsSingleCategoryDiagramRenderer::readXml( const QDomElement &elem, const QgsReadWriteContext &context )
731{
732 const QDomElement categoryElem = elem.firstChildElement( u"DiagramCategory"_s );
733 if ( categoryElem.isNull() )
734 {
735 return;
736 }
737
738 mSettings.readXml( categoryElem, context );
739 _readXml( elem, context );
740}
741
742void QgsSingleCategoryDiagramRenderer::writeXml( QDomElement &layerElem, QDomDocument &doc, const QgsReadWriteContext &context ) const
743{
744 QDomElement rendererElem = doc.createElement( u"SingleCategoryDiagramRenderer"_s );
745 mSettings.writeXml( rendererElem, doc, context );
746 _writeXml( rendererElem, doc, context );
747 layerElem.appendChild( rendererElem );
748}
749
751
753{
754 mInterpolationSettings.classificationAttributeIsExpression = false;
755}
756
758 : QgsDiagramRenderer( other )
759 , mSettings( other.mSettings )
760 , mInterpolationSettings( other.mInterpolationSettings )
761 , mDataDefinedSizeLegend( other.mDataDefinedSizeLegend ? new QgsDataDefinedSizeLegend( *other.mDataDefinedSizeLegend ) : nullptr )
762{
763}
764
769
771{
772 if ( &other == this )
773 {
774 return *this;
775 }
776 mSettings = other.mSettings;
777 mInterpolationSettings = other.mInterpolationSettings;
778 mDataDefinedSizeLegend = std::make_unique<QgsDataDefinedSizeLegend>( *other.mDataDefinedSizeLegend );
779
780 return *this;
781}
782
787
789{
790 QList<QgsDiagramSettings> settingsList;
791 settingsList.push_back( mSettings );
792 return settingsList;
793}
794
796{
797 s = mSettings;
798 s.size = diagramSize( feature, c );
799 return true;
800}
801
803{
804 return mSettings.categoryAttributes;
805}
806
808{
809 QSet< QString > referenced = QgsDiagramRenderer::referencedFields( context );
810 if ( mInterpolationSettings.classificationAttributeIsExpression )
811 {
812 QgsExpression *expression = mDiagram->getExpression( mInterpolationSettings.classificationAttributeExpression, context );
813 const auto constReferencedColumns = expression->referencedColumns();
814 for ( const QString &field : constReferencedColumns )
815 {
816 referenced << field;
817 }
818 }
819 else
820 {
821 referenced << mInterpolationSettings.classificationField;
822 }
823 return referenced;
824}
825
827{
828 return mDiagram->diagramSize( feature, c, mSettings, mInterpolationSettings );
829}
830
831void QgsLinearlyInterpolatedDiagramRenderer::readXml( const QDomElement &elem, const QgsReadWriteContext &context )
832{
833 mInterpolationSettings.lowerValue = elem.attribute( u"lowerValue"_s ).toDouble();
834 mInterpolationSettings.upperValue = elem.attribute( u"upperValue"_s ).toDouble();
835 mInterpolationSettings.lowerSize.setWidth( elem.attribute( u"lowerWidth"_s ).toDouble() );
836 mInterpolationSettings.lowerSize.setHeight( elem.attribute( u"lowerHeight"_s ).toDouble() );
837 mInterpolationSettings.upperSize.setWidth( elem.attribute( u"upperWidth"_s ).toDouble() );
838 mInterpolationSettings.upperSize.setHeight( elem.attribute( u"upperHeight"_s ).toDouble() );
839 mInterpolationSettings.classificationAttributeIsExpression = elem.hasAttribute( u"classificationAttributeExpression"_s );
840 if ( mInterpolationSettings.classificationAttributeIsExpression )
841 {
842 mInterpolationSettings.classificationAttributeExpression = elem.attribute( u"classificationAttributeExpression"_s );
843 }
844 else
845 {
846 mInterpolationSettings.classificationField = elem.attribute( u"classificationField"_s );
847 }
848 const QDomElement settingsElem = elem.firstChildElement( u"DiagramCategory"_s );
849 if ( !settingsElem.isNull() )
850 {
851 mSettings.readXml( settingsElem );
852 }
853
854 mDataDefinedSizeLegend.reset( );
855
856 const QDomElement ddsLegendSizeElem = elem.firstChildElement( u"data-defined-size-legend"_s );
857 if ( !ddsLegendSizeElem.isNull() )
858 {
859 mDataDefinedSizeLegend.reset( QgsDataDefinedSizeLegend::readXml( ddsLegendSizeElem, context ) );
860 }
861 else
862 {
863 // pre-3.0 projects
864 if ( elem.attribute( u"sizeLegend"_s, u"0"_s ) != "0"_L1 )
865 {
866 mDataDefinedSizeLegend = std::make_unique<QgsDataDefinedSizeLegend>();
867 const QDomElement sizeLegendSymbolElem = elem.firstChildElement( u"symbol"_s );
868 if ( !sizeLegendSymbolElem.isNull() && sizeLegendSymbolElem.attribute( u"name"_s ) == "sizeSymbol"_L1 )
869 {
870 mDataDefinedSizeLegend->setSymbol( QgsSymbolLayerUtils::loadSymbol<QgsMarkerSymbol>( sizeLegendSymbolElem, context ).release() );
871 }
872 }
873 else
874 {
875 mDataDefinedSizeLegend = nullptr;
876 }
877 }
878
879 _readXml( elem, context );
880}
881
882void QgsLinearlyInterpolatedDiagramRenderer::writeXml( QDomElement &layerElem, QDomDocument &doc, const QgsReadWriteContext &context ) const
883{
884 QDomElement rendererElem = doc.createElement( u"LinearlyInterpolatedDiagramRenderer"_s );
885 rendererElem.setAttribute( u"lowerValue"_s, QString::number( mInterpolationSettings.lowerValue ) );
886 rendererElem.setAttribute( u"upperValue"_s, QString::number( mInterpolationSettings.upperValue ) );
887 rendererElem.setAttribute( u"lowerWidth"_s, QString::number( mInterpolationSettings.lowerSize.width() ) );
888 rendererElem.setAttribute( u"lowerHeight"_s, QString::number( mInterpolationSettings.lowerSize.height() ) );
889 rendererElem.setAttribute( u"upperWidth"_s, QString::number( mInterpolationSettings.upperSize.width() ) );
890 rendererElem.setAttribute( u"upperHeight"_s, QString::number( mInterpolationSettings.upperSize.height() ) );
891 if ( mInterpolationSettings.classificationAttributeIsExpression )
892 {
893 rendererElem.setAttribute( u"classificationAttributeExpression"_s, mInterpolationSettings.classificationAttributeExpression );
894 }
895 else
896 {
897 rendererElem.setAttribute( u"classificationField"_s, mInterpolationSettings.classificationField );
898 }
899 mSettings.writeXml( rendererElem, doc );
900
901 if ( mDataDefinedSizeLegend )
902 {
903 QDomElement ddsLegendElem = doc.createElement( u"data-defined-size-legend"_s );
904 mDataDefinedSizeLegend->writeXml( ddsLegendElem, context );
905 rendererElem.appendChild( ddsLegendElem );
906 }
907
908 _writeXml( rendererElem, doc, context );
909 layerElem.appendChild( rendererElem );
910}
911
913
915 : QgsDiagramRenderer( other )
916 , mSettings( other.mSettings )
917 , mDiagramRenderers()
918{
919 for ( QgsDiagramRenderer *renderer : std::as_const( other.mDiagramRenderers ) )
920 {
921 if ( renderer )
922 mDiagramRenderers << renderer->clone();
923 }
924}
925
927{
928 if ( &other == this )
929 return *this;
930
931 mSettings = other.mSettings;
932 qDeleteAll( mDiagramRenderers );
933 mDiagramRenderers.clear();
934 for ( QgsDiagramRenderer *renderer : std::as_const( other.mDiagramRenderers ) )
935 {
936 if ( renderer )
937 mDiagramRenderers << renderer->clone();
938 }
939
940 return *this;
941}
942
944{
945 qDeleteAll( mDiagramRenderers );
946}
947
952
954{
955 QSizeF stackedSize( 0, 0 );
956 int enabledDiagramCount = 0; // We'll add spacing only for enabled subDiagrams
957
958 // Iterate renderers. For each renderer, get the diagram
959 // size for the feature and add it to the total size
960 // accounting for stacked diagram defined spacing
961 for ( const QgsDiagramRenderer *subRenderer : std::as_const( mDiagramRenderers ) )
962 {
963 QSizeF size = subRenderer->sizeMapUnits( feature, c );
964
965 if ( size.isValid() )
966 {
967 enabledDiagramCount++;
968 switch ( mSettings.stackedDiagramMode )
969 {
971 stackedSize.setWidth( stackedSize.width() + size.width() );
972 stackedSize.setHeight( std::max( stackedSize.height(), size.height() ) );
973 break;
974
976 stackedSize.setWidth( std::max( stackedSize.width(), size.width() ) );
977 stackedSize.setHeight( stackedSize.height() + size.height() );
978 break;
979 }
980 }
981 }
982
983 if ( stackedSize.isValid() )
984 {
985 const double spacing = c.convertToMapUnits( mSettings.stackedDiagramSpacing(), mSettings.stackedDiagramSpacingUnit(), mSettings.stackedDiagramSpacingMapUnitScale() );
986
987 switch ( mSettings.stackedDiagramMode )
988 {
990 stackedSize.scale( stackedSize.width() + spacing * ( enabledDiagramCount - 1 ), stackedSize.height(), Qt::IgnoreAspectRatio );
991 break;
992
994 stackedSize.scale( stackedSize.width(), stackedSize.height() + spacing * ( enabledDiagramCount - 1 ), Qt::IgnoreAspectRatio );
995 break;
996 }
997 }
998 return stackedSize;
999}
1000
1001void QgsStackedDiagramRenderer::renderDiagram( const QgsFeature &feature, QgsRenderContext &c, QPointF pos, const QgsPropertyCollection &properties ) const
1002{
1003 if ( !mDiagram )
1004 {
1005 return;
1006 }
1007
1008 QPointF newPos = pos; // Each subdiagram will have its own newPos
1009
1010 // Get subrenderers sorted by mode (vertical diagrams are returned backwards)
1011 const QList< QgsDiagramRenderer * > stackedRenderers = renderers( true );
1012
1013 for ( const QgsDiagramRenderer *stackedRenderer : stackedRenderers )
1014 {
1015 if ( stackedRenderer->rendererName() == QgsStackedDiagramRenderer::DIAGRAM_RENDERER_NAME_STACKED )
1016 {
1017 // Nested stacked diagrams will use this recursion
1018 stackedRenderer->renderDiagram( feature, c, newPos, properties );
1019 continue;
1020 }
1021
1023 if ( !stackedRenderer->diagramSettings( feature, c, s ) )
1024 {
1025 continue;
1026 }
1027
1028 if ( !s.enabled )
1029 {
1030 continue;
1031 }
1032
1033 if ( s.scaleBasedVisibility )
1034 {
1035 // Note: scale might be a non-round number, so compare with qgsDoubleNear
1036 const double rendererScale = c.rendererScale();
1037
1038 // maxScale is inclusive ( < --> no diagram )
1039 double maxScale = s.maximumScale;
1040 if ( maxScale > 0 && QgsScaleUtils::lessThanMaximumScale( rendererScale, maxScale ) )
1041 {
1042 continue;
1043 }
1044
1045 // minScale is exclusive ( >= --> no diagram)
1046 double minScale = s.minimumScale;
1047 if ( minScale > 0 && QgsScaleUtils::equalToOrGreaterThanMinimumScale( rendererScale, minScale ) )
1048 {
1049 continue;
1050 }
1051 }
1052
1053 if ( properties.hasActiveProperties() )
1054 {
1055 c.expressionContext().setOriginalValueVariable( QgsColorUtils::colorToString( s.backgroundColor ) );
1057 c.expressionContext().setOriginalValueVariable( QgsColorUtils::colorToString( s.penColor ) );
1058 s.penColor = properties.valueAsColor( QgsDiagramLayerSettings::Property::StrokeColor, c.expressionContext(), s.penColor );
1059 c.expressionContext().setOriginalValueVariable( s.penWidth );
1060 s.penWidth = properties.valueAsDouble( QgsDiagramLayerSettings::Property::StrokeWidth, c.expressionContext(), s.penWidth );
1061 c.expressionContext().setOriginalValueVariable( s.rotationOffset );
1063 }
1064
1065 QgsPaintEffect *effect = s.paintEffect();
1066 std::unique_ptr< QgsEffectPainter > effectPainter;
1067 if ( effect && effect->enabled() )
1068 {
1069 effectPainter = std::make_unique< QgsEffectPainter >( c, effect );
1070 }
1071
1072 stackedRenderer->diagram()->renderDiagram( feature, c, s, newPos );
1073 QgsStackedDiagram *stackedDiagram = dynamic_cast< QgsStackedDiagram *>( mDiagram.get() );
1074 stackedDiagram->subDiagramPosition( newPos, c, mSettings, s );
1075 }
1076}
1077
1079{
1080 Q_UNUSED( feature )
1081 Q_UNUSED( c )
1082 Q_UNUSED( s )
1083 return false;
1084}
1085
1087{
1088 Q_UNUSED( feature )
1089 Q_UNUSED( c )
1090 return QSizeF( 0, 0 );
1091}
1092
1093QList<QgsDiagramSettings> QgsStackedDiagramRenderer::diagramSettings() const
1094{
1095 QList<QgsDiagramSettings> settingsList;
1096 settingsList.push_back( mSettings );
1097 return settingsList;
1098}
1099
1101{
1102 return mSettings.categoryAttributes;
1103}
1104
1105QList< QgsLayerTreeModelLegendNode * > QgsStackedDiagramRenderer::legendItems( QgsLayerTreeLayer *nodeLayer ) const
1106{
1107 QList< QgsLayerTreeModelLegendNode * > nodes;
1108 for ( const QgsDiagramRenderer *renderer : std::as_const( mDiagramRenderers ) )
1109 {
1110 nodes << renderer->legendItems( nodeLayer );
1111 }
1112
1113 return nodes;
1114}
1115
1116QList< QgsDiagramRenderer * > QgsStackedDiagramRenderer::renderers( bool sortByDiagramMode ) const
1117{
1118 QList< QgsDiagramRenderer * > renderers = mDiagramRenderers;
1119
1120 if ( sortByDiagramMode && mSettings.stackedDiagramMode == QgsDiagramSettings::Vertical )
1121 {
1122 // We draw vertical diagrams backwards, so
1123 // we return the subrenderers in reverse order
1124 std::reverse( renderers.begin(), renderers.end() );
1125 }
1126 return renderers;
1127}
1128
1130{
1131 if ( renderer )
1132 {
1133 mDiagramRenderers.append( renderer );
1134 }
1135}
1136
1138{
1139 return mDiagramRenderers.value( index );
1140}
1141
1143{
1144 return mDiagramRenderers.size();
1145}
1146
1147void QgsStackedDiagramRenderer::readXml( const QDomElement &elem, const QgsReadWriteContext &context )
1148{
1149 const QDomElement categoryElem = elem.firstChildElement( u"DiagramCategory"_s );
1150 if ( categoryElem.isNull() )
1151 {
1152 return;
1153 }
1154
1155 mSettings.readXml( categoryElem, context );
1156 _readXml( elem, context );
1157 _readXmlSubRenderers( elem, context );
1158}
1159
1160void QgsStackedDiagramRenderer::_readXmlSubRenderers( const QDomElement &elem, const QgsReadWriteContext &context )
1161{
1162 qDeleteAll( mDiagramRenderers );
1163 mDiagramRenderers.clear();
1164
1165 const QDomElement subRenderersElem = elem.firstChildElement( u"DiagramRenderers"_s );
1166
1167 if ( !subRenderersElem.isNull() )
1168 {
1169 const QDomNodeList childRendererList = subRenderersElem.childNodes();
1170
1171 for ( int i = 0; i < childRendererList.size(); i++ )
1172 {
1173 const QDomElement subRendererElem = childRendererList.at( i ).toElement();
1174
1175 if ( subRendererElem.nodeName() == "SingleCategoryDiagramRenderer"_L1 )
1176 {
1177 auto singleCatDiagramRenderer = std::make_unique< QgsSingleCategoryDiagramRenderer >();
1178 singleCatDiagramRenderer->readXml( subRendererElem, context );
1179 addRenderer( singleCatDiagramRenderer.release() );
1180 }
1181 else if ( subRendererElem.nodeName() == "LinearlyInterpolatedDiagramRenderer"_L1 )
1182 {
1183 auto linearDiagramRenderer = std::make_unique< QgsLinearlyInterpolatedDiagramRenderer >();
1184 linearDiagramRenderer->readXml( subRendererElem, context );
1185 addRenderer( linearDiagramRenderer.release() );
1186 }
1187 else if ( subRendererElem.nodeName() == "StackedDiagramRenderer"_L1 )
1188 {
1189 auto stackedDiagramRenderer = std::make_unique< QgsStackedDiagramRenderer >();
1190 stackedDiagramRenderer->readXml( subRendererElem, context );
1191 addRenderer( stackedDiagramRenderer.release() );
1192 }
1193 }
1194 }
1195}
1196
1197void QgsStackedDiagramRenderer::writeXml( QDomElement &layerElem, QDomDocument &doc, const QgsReadWriteContext &context ) const
1198{
1199 QDomElement rendererElem = doc.createElement( u"StackedDiagramRenderer"_s );
1200 mSettings.writeXml( rendererElem, doc, context );
1201 _writeXml( rendererElem, doc, context );
1202 _writeXmlSubRenderers( rendererElem, doc, context );
1203 layerElem.appendChild( rendererElem );
1204}
1205
1206void QgsStackedDiagramRenderer::_writeXmlSubRenderers( QDomElement &rendererElem, QDomDocument &doc, const QgsReadWriteContext &context ) const
1207{
1208 QDomElement renderersElem = doc.createElement( u"DiagramRenderers"_s );
1209
1210 // Iterate sub renderers and write their settings to a DOM object
1211 for ( int i = 0; i < mDiagramRenderers.count(); i++ )
1212 {
1213 mDiagramRenderers.at( i )->writeXml( renderersElem, doc, context );
1214 }
1215 rendererElem.appendChild( renderersElem );
1216}
1217
1218QList< QgsLayerTreeModelLegendNode * > QgsDiagramSettings::legendItems( QgsLayerTreeLayer *nodeLayer ) const
1219{
1220 QList< QgsLayerTreeModelLegendNode * > list;
1221 list.reserve( categoryLabels.size() );
1222 for ( int i = 0; i < categoryLabels.size(); ++i )
1223 {
1224 QPixmap pix( 16, 16 );
1225 pix.fill( categoryColors[i] );
1226 list << new QgsSimpleLegendNode( nodeLayer, categoryLabels[i], QIcon( pix ), nullptr, u"diagram_%1"_s.arg( QString::number( i ) ) );
1227 }
1228 return list;
1229}
1230
1232{
1233 return mAxisLineSymbol.get();
1234}
1235
1237{
1238 if ( axisLineSymbol != mAxisLineSymbol.get() )
1239 mAxisLineSymbol.reset( axisLineSymbol );
1240}
1241
1243{
1244 return mShowAxis;
1245}
1246
1248{
1249 mShowAxis = showAxis;
1250}
1251
1253{
1254 return mPaintEffect.get();
1255}
1256
1258{
1259 if ( effect != mPaintEffect.get() )
1260 mPaintEffect.reset( effect );
1261}
1262
1264 : mAxisLineSymbol( std::make_unique< QgsLineSymbol >() )
1265{
1266}
1267
1269
1271 : enabled( other.enabled )
1272 , font( other.font )
1276 , size( other.size )
1277 , sizeType( other.sizeType )
1278 , sizeScale( other.sizeScale )
1279 , lineSizeUnit( other.lineSizeUnit )
1280 , lineSizeScale( other.lineSizeScale )
1282 , penColor( other.penColor )
1283 , penWidth( other.penWidth )
1287 , barWidth( other.barWidth )
1288 , opacity( other.opacity )
1289 , scaleByArea( other.scaleByArea )
1292 , maximumScale( other.maximumScale )
1293 , minimumScale( other.minimumScale )
1294 , minimumSize( other.minimumSize )
1295 , mSpacing( other.mSpacing )
1296 , mSpacingUnit( other.mSpacingUnit )
1297 , mSpacingMapUnitScale( other.mSpacingMapUnitScale )
1298 , mStackedDiagramSpacing( other.mStackedDiagramSpacing )
1299 , mStackedDiagramSpacingUnit( other.mStackedDiagramSpacingUnit )
1300 , mStackedDiagramSpacingMapUnitScale( other.mStackedDiagramSpacingMapUnitScale )
1301 , mDirection( other.mDirection )
1302 , mShowAxis( other.mShowAxis )
1303 , mAxisLineSymbol( other.mAxisLineSymbol ? other.mAxisLineSymbol->clone() : nullptr )
1304 , mPaintEffect( other.mPaintEffect ? other.mPaintEffect->clone() : nullptr )
1305{
1306
1307}
1308
1310{
1311 if ( &other == this )
1312 return *this;
1313
1314 enabled = other.enabled;
1315 font = other.font;
1319 size = other.size;
1320 sizeType = other.sizeType;
1321 sizeScale = other.sizeScale;
1322 lineSizeUnit = other.lineSizeUnit;
1325 penColor = other.penColor;
1326 penWidth = other.penWidth;
1330 barWidth = other.barWidth;
1331 opacity = other.opacity;
1332 scaleByArea = other.scaleByArea;
1335 maximumScale = other.maximumScale;
1336 minimumScale = other.minimumScale;
1337 minimumSize = other.minimumSize;
1338 mSpacing = other.mSpacing;
1339 mSpacingUnit = other.mSpacingUnit;
1340 mSpacingMapUnitScale = other.mSpacingMapUnitScale;
1341 mStackedDiagramSpacing = other.mStackedDiagramSpacing;
1342 mStackedDiagramSpacingUnit = other.mStackedDiagramSpacingUnit;
1343 mStackedDiagramSpacingMapUnitScale = other.mStackedDiagramSpacingMapUnitScale;
1344 mDirection = other.mDirection;
1345 mAxisLineSymbol.reset( other.mAxisLineSymbol ? other.mAxisLineSymbol->clone() : nullptr );
1346 mShowAxis = other.mShowAxis;
1347 mPaintEffect.reset( other.mPaintEffect ? other.mPaintEffect->clone() : nullptr );
1348 return *this;
1349}
1350
1352{
1353 return mDirection;
1354}
1355
1357{
1358 mDirection = direction;
1359}
1360
1361QList< QgsLayerTreeModelLegendNode * > QgsDiagramRenderer::legendItems( QgsLayerTreeLayer * ) const
1362{
1363 return QList< QgsLayerTreeModelLegendNode * >();
1364}
1365
1366QList< QgsLayerTreeModelLegendNode * > QgsSingleCategoryDiagramRenderer::legendItems( QgsLayerTreeLayer *nodeLayer ) const
1367{
1368 QList< QgsLayerTreeModelLegendNode * > nodes;
1369 if ( mShowAttributeLegend && mSettings.enabled )
1370 nodes = mSettings.legendItems( nodeLayer );
1371
1372 return nodes;
1373}
1374
1375QList< QgsLayerTreeModelLegendNode * > QgsLinearlyInterpolatedDiagramRenderer::legendItems( QgsLayerTreeLayer *nodeLayer ) const
1376{
1377 QList< QgsLayerTreeModelLegendNode * > nodes;
1378 if ( !mSettings.enabled )
1379 {
1380 return nodes;
1381 }
1382
1384 nodes = mSettings.legendItems( nodeLayer );
1385
1386 if ( mDataDefinedSizeLegend && mDiagram )
1387 {
1388 // add size legend
1389 QgsMarkerSymbol *legendSymbol = mDataDefinedSizeLegend->symbol() ? mDataDefinedSizeLegend->symbol()->clone() : QgsMarkerSymbol::createSimple( QVariantMap() ).release();
1390 legendSymbol->setSizeUnit( mSettings.sizeType );
1391 legendSymbol->setSizeMapUnitScale( mSettings.sizeScale );
1392
1393 QgsDataDefinedSizeLegend ddSizeLegend( *mDataDefinedSizeLegend );
1394 ddSizeLegend.setSymbol( legendSymbol ); // transfers ownership
1395
1396 QList<QgsDataDefinedSizeLegend::SizeClass> sizeClasses;
1397 if ( ddSizeLegend.classes().isEmpty() )
1398 {
1399 // automatic class creation if the classes are not defined manually
1400 const auto prettyBreaks { QgsSymbolLayerUtils::prettyBreaks( mInterpolationSettings.lowerValue, mInterpolationSettings.upperValue, 4 ) };
1401 for ( const double v : prettyBreaks )
1402 {
1403 const double size = mDiagram->legendSize( v, mSettings, mInterpolationSettings );
1404 sizeClasses << QgsDataDefinedSizeLegend::SizeClass( size, QString::number( v ) );
1405 }
1406 }
1407 else
1408 {
1409 // manual classes need to get size scaled because the QgsSizeScaleTransformer is not used in diagrams :-(
1410 const auto constClasses = ddSizeLegend.classes();
1411 for ( const QgsDataDefinedSizeLegend::SizeClass &sc : constClasses )
1412 {
1413 const double size = mDiagram->legendSize( sc.size, mSettings, mInterpolationSettings );
1414 sizeClasses << QgsDataDefinedSizeLegend::SizeClass( size, sc.label );
1415 }
1416 }
1417 ddSizeLegend.setClasses( sizeClasses );
1418
1419 const auto constLegendSymbolList = ddSizeLegend.legendSymbolList();
1420 for ( const QgsLegendSymbolItem &si : constLegendSymbolList )
1421 {
1422 if ( auto *lDataDefinedSizeLegendSettings = si.dataDefinedSizeLegendSettings() )
1423 nodes << new QgsDataDefinedSizeLegendNode( nodeLayer, *lDataDefinedSizeLegendSettings );
1424 else
1425 nodes << new QgsSymbolLegendNode( nodeLayer, si );
1426 }
1427 }
1428
1429 return nodes;
1430}
1431
1433{
1434 mDataDefinedSizeLegend.reset( settings );
1435
1436}
1437
1439{
1440 return mDataDefinedSizeLegend.get();
1441}
@ MapUnits
Map units.
Definition qgis.h:5257
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:57
@ StrokeWidth
Line stroke width.
Definition qgsproperty.h:72
@ Boolean
Boolean value.
Definition qgsproperty.h:53
@ Rotation
Rotation (value between 0-360 degrees).
Definition qgsproperty.h:60
@ ColorWithAlpha
Color with alpha channel.
Definition qgsproperty.h:64
@ DoublePositive
Positive double value (including 0).
Definition qgsproperty.h:58
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.