QGIS API Documentation 4.0.0-Norrköping (1ddcee3d0e4)
Loading...
Searching...
No Matches
qgsplot.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsplot.cpp
3 ---------------
4 begin : March 2022
5 copyright : (C) 2022 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18#include "qgsplot.h"
19
20#include <functional>
21
22#include "qgsapplication.h"
24#include "qgscolorramp.h"
25#include "qgscolorrampimpl.h"
27#include "qgsfillsymbol.h"
28#include "qgsfillsymbollayer.h"
29#include "qgslinesymbol.h"
30#include "qgslinesymbollayer.h"
31#include "qgsmarkersymbol.h"
34#include "qgssymbollayerutils.h"
35#include "qgstextrenderer.h"
36
37#include <QString>
38
39using namespace Qt::StringLiterals;
40
41QgsPropertiesDefinition QgsPlot::sPropertyDefinitions;
42
43QgsPlot::~QgsPlot() = default;
44
45bool QgsPlot::writeXml( QDomElement &element, QDomDocument &document, const QgsReadWriteContext & ) const
46{
47 element.setAttribute( u"plotType"_s, type() );
48
49 QDomElement dataDefinedPropertiesElement = document.createElement( u"dataDefinedProperties"_s );
50 mDataDefinedProperties.writeXml( dataDefinedPropertiesElement, QgsPlot::propertyDefinitions() );
51 element.appendChild( dataDefinedPropertiesElement );
52
53 return true;
54}
55
56bool QgsPlot::readXml( const QDomElement &element, const QgsReadWriteContext & )
57{
58 QDomElement dataDefinedPropertiesElement = element.firstChildElement( u"dataDefinedProperties"_s );
59 mDataDefinedProperties.readXml( dataDefinedPropertiesElement, QgsPlot::propertyDefinitions() );
60
61 return true;
62}
63
64void QgsPlot::initPropertyDefinitions()
65{
66 if ( !sPropertyDefinitions.isEmpty() )
67 return;
68
69 sPropertyDefinitions = QgsPropertiesDefinition {
70 { static_cast< int >( QgsPlot::DataDefinedProperty::MarginLeft ), QgsPropertyDefinition( "dataDefinedPlotMarginLeft", QObject::tr( "Left margin" ), QgsPropertyDefinition::DoublePositive ) },
71 { static_cast< int >( QgsPlot::DataDefinedProperty::MarginTop ), QgsPropertyDefinition( "dataDefinedPlotMarginTop", QObject::tr( "Top margin" ), QgsPropertyDefinition::DoublePositive ) },
72 { static_cast< int >( QgsPlot::DataDefinedProperty::MarginRight ), QgsPropertyDefinition( "dataDefinedPlotMarginRight", QObject::tr( "Right margin" ), QgsPropertyDefinition::DoublePositive ) },
73 { static_cast< int >( QgsPlot::DataDefinedProperty::MarginBottom ), QgsPropertyDefinition( "dataDefinedPlotMarginBottom", QObject::tr( "Bottom margin" ), QgsPropertyDefinition::DoublePositive ) },
75 QgsPropertyDefinition( "dataDefinedPlotXAxisMajorInterval", QObject::tr( "Major grid line interval for X-axis" ), QgsPropertyDefinition::DoublePositive ) },
77 QgsPropertyDefinition( "dataDefinedPlotXAxisMinorInterval", QObject::tr( "Minor grid line interval for X-axis" ), QgsPropertyDefinition::DoublePositive ) },
79 QgsPropertyDefinition( "dataDefinedPlotXAxisLabelInterval", QObject::tr( "Label interval for X-axis" ), QgsPropertyDefinition::DoublePositive ) },
81 QgsPropertyDefinition( "dataDefinedPlotYAxisMajorInterval", QObject::tr( "Major grid line interval for Y-axis" ), QgsPropertyDefinition::DoublePositive ) },
83 QgsPropertyDefinition( "dataDefinedPlotYAxisMinorInterval", QObject::tr( "Minor grid line interval for Y-axis" ), QgsPropertyDefinition::DoublePositive ) },
85 QgsPropertyDefinition( "dataDefinedPlotYAxisLabelInterval", QObject::tr( "Label interval for Y-axis" ), QgsPropertyDefinition::DoublePositive ) },
86 { static_cast< int >( QgsPlot::DataDefinedProperty::XAxisMinimum ),
87 QgsPropertyDefinition( "dataDefinedPlotXAxisMinimum", QObject::tr( "X-axis minimum value" ), QgsPropertyDefinition::DoublePositive ) },
88 { static_cast< int >( QgsPlot::DataDefinedProperty::XAxisMaximum ),
89 QgsPropertyDefinition( "dataDefinedPlotXAxisMaximum", QObject::tr( "X-axis maximum value" ), QgsPropertyDefinition::DoublePositive ) },
90 { static_cast< int >( QgsPlot::DataDefinedProperty::YAxisMinimum ), QgsPropertyDefinition( "dataDefinedPlotYAxisMinimum", QObject::tr( "Y-axis minimum value" ), QgsPropertyDefinition::Double ) },
91 { static_cast< int >( QgsPlot::DataDefinedProperty::YAxisMaximum ), QgsPropertyDefinition( "dataDefinedPlotYAxisMaximum", QObject::tr( "Y-axis maximum value" ), QgsPropertyDefinition::Double ) },
92 };
93}
94
96{
97 QgsPlot::initPropertyDefinitions();
98 return sPropertyDefinitions;
99}
100
102{
103 if ( !plot )
104 return;
106}
107
108
109//
110// QgsPlotAxis
111//
112
114{
115 // setup default style
116 mNumericFormat.reset( QgsPlotDefaultSettings::axisLabelNumericFormat() );
117 mGridMinorSymbol.reset( QgsPlotDefaultSettings::axisGridMinorSymbol() );
118 mGridMajorSymbol.reset( QgsPlotDefaultSettings::axisGridMajorSymbol() );
119}
120
121QgsPlotAxis::~QgsPlotAxis() = default;
122
123
125{
126 return mType;
127}
128
130{
131 mType = type;
132}
133
134bool QgsPlotAxis::writeXml( QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context ) const
135{
136 element.setAttribute( u"type"_s, qgsEnumValueToKey( mType ) );
137 element.setAttribute( u"gridIntervalMinor"_s, qgsDoubleToString( mGridIntervalMinor ) );
138 element.setAttribute( u"gridIntervalMajor"_s, qgsDoubleToString( mGridIntervalMajor ) );
139 element.setAttribute( u"labelInterval"_s, qgsDoubleToString( mLabelInterval ) );
140 element.setAttribute( u"suffix"_s, mLabelSuffix );
141 element.setAttribute( u"suffixPlacement"_s, qgsEnumValueToKey( mSuffixPlacement ) );
142
143 QDomElement numericFormatElement = document.createElement( u"numericFormat"_s );
144 mNumericFormat->writeXml( numericFormatElement, document, context );
145 element.appendChild( numericFormatElement );
146
147 QDomElement gridMajorElement = document.createElement( u"gridMajorSymbol"_s );
148 gridMajorElement.appendChild( QgsSymbolLayerUtils::saveSymbol( QString(), mGridMajorSymbol.get(), document, context ) );
149 element.appendChild( gridMajorElement );
150 QDomElement gridMinorElement = document.createElement( u"gridMinorSymbol"_s );
151 gridMinorElement.appendChild( QgsSymbolLayerUtils::saveSymbol( QString(), mGridMinorSymbol.get(), document, context ) );
152 element.appendChild( gridMinorElement );
153
154 QDomElement textFormatElement = document.createElement( u"textFormat"_s );
155 textFormatElement.appendChild( mLabelTextFormat.writeXml( document, context ) );
156 element.appendChild( textFormatElement );
157
158 return true;
159}
160
161bool QgsPlotAxis::readXml( const QDomElement &element, const QgsReadWriteContext &context )
162{
163 mType = qgsEnumKeyToValue( element.attribute( u"type"_s ), Qgis::PlotAxisType::Interval );
164 mGridIntervalMinor = element.attribute( u"gridIntervalMinor"_s ).toDouble();
165 mGridIntervalMajor = element.attribute( u"gridIntervalMajor"_s ).toDouble();
166 mLabelInterval = element.attribute( u"labelInterval"_s ).toDouble();
167
168 mLabelSuffix = element.attribute( u"suffix"_s );
169 mSuffixPlacement = qgsEnumKeyToValue( element.attribute( u"suffixPlacement"_s ), Qgis::PlotAxisSuffixPlacement::NoLabels );
170
171 const QDomElement numericFormatElement = element.firstChildElement( u"numericFormat"_s );
172 mNumericFormat.reset( QgsApplication::numericFormatRegistry()->createFromXml( numericFormatElement, context ) );
173
174 const QDomElement gridMajorElement = element.firstChildElement( u"gridMajorSymbol"_s ).firstChildElement( u"symbol"_s );
175 mGridMajorSymbol = QgsSymbolLayerUtils::loadSymbol< QgsLineSymbol >( gridMajorElement, context );
176 const QDomElement gridMinorElement = element.firstChildElement( u"gridMinorSymbol"_s ).firstChildElement( u"symbol"_s );
177 mGridMinorSymbol = QgsSymbolLayerUtils::loadSymbol< QgsLineSymbol >( gridMinorElement, context );
178
179 const QDomElement textFormatElement = element.firstChildElement( u"textFormat"_s );
180 mLabelTextFormat.readXml( textFormatElement, context );
181
182 return true;
183}
184
186{
187 return mNumericFormat.get();
188}
189
191{
192 mNumericFormat.reset( format );
193}
194
196{
197 return mLabelSuffix;
198}
199
200void QgsPlotAxis::setLabelSuffix( const QString &suffix )
201{
202 mLabelSuffix = suffix;
203}
204
206{
207 return mSuffixPlacement;
208}
209
211{
212 mSuffixPlacement = placement;
213}
214
216{
217 return mGridMajorSymbol.get();
218}
219
221{
222 return mGridMajorSymbol.get();
223}
224
226{
227 mGridMajorSymbol.reset( symbol );
228}
229
231{
232 return mGridMinorSymbol.get();
233}
234
236{
237 return mGridMinorSymbol.get();
238}
239
241{
242 mGridMinorSymbol.reset( symbol );
243}
244
246{
247 return mLabelTextFormat;
248}
249
251{
252 mLabelTextFormat = format;
253}
254
256{
257 dest.setType( source.type() );
260 dest.setLabelInterval( source.labelInterval() );
261 dest.setLabelSuffix( source.labelSuffix() );
263 dest.setTextFormat( source.textFormat() );
264 if ( source.numericFormat() )
265 dest.setNumericFormat( source.numericFormat()->clone() );
266 if ( source.gridMajorSymbol() )
267 dest.setGridMajorSymbol( source.gridMajorSymbol()->clone() );
268 if ( source.gridMinorSymbol() )
269 dest.setGridMinorSymbol( source.gridMinorSymbol()->clone() );
270}
271
272
273//
274// Qgs2DPlot
275//
276
278 : mMargins( 2, 2, 2, 2 )
279{}
280
281bool Qgs2DPlot::writeXml( QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context ) const
282{
283 QgsPlot::writeXml( element, document, context );
284
285 element.setAttribute( u"margins"_s, mMargins.toString() );
286
287 return true;
288}
289
290bool Qgs2DPlot::readXml( const QDomElement &element, const QgsReadWriteContext &context )
291{
292 QgsPlot::readXml( element, context );
293
294 mMargins = QgsMargins::fromString( element.attribute( u"margins"_s ) );
295
296 return true;
297}
298
299void Qgs2DPlot::render( QgsRenderContext &context, QgsPlotRenderContext &plotContext, const QgsPlotData &plotData )
300{
301 QgsExpressionContextScope *plotScope = new QgsExpressionContextScope( u"plot"_s );
302 const QgsExpressionContextScopePopper scopePopper( context.expressionContext(), plotScope );
303
304 const QRectF plotArea = interiorPlotArea( context, plotContext );
305
306 // give subclasses a chance to draw their content
307 renderContent( context, plotContext, plotArea, plotData );
308}
309
312
313Qgs2DPlot::~Qgs2DPlot() = default;
314
315QSizeF Qgs2DPlot::size() const
316{
317 return mSize;
318}
319
321{
322 mSize = size;
323}
324
326{
327 QgsMargins usedMargins = mMargins;
328 applyDataDefinedProperties( context, usedMargins );
329
330 const double leftMargin = context.convertToPainterUnits( usedMargins.left(), Qgis::RenderUnit::Millimeters );
331 const double rightMargin = context.convertToPainterUnits( usedMargins.right(), Qgis::RenderUnit::Millimeters );
332 const double topMargin = context.convertToPainterUnits( usedMargins.top(), Qgis::RenderUnit::Millimeters );
333 const double bottomMargin = context.convertToPainterUnits( usedMargins.bottom(), Qgis::RenderUnit::Millimeters );
334
335 return QRectF( leftMargin, topMargin, mSize.width() - rightMargin - leftMargin, mSize.height() - bottomMargin - topMargin );
336}
337
339{
340 return mMargins;
341}
342
344{
345 mMargins = margins;
346}
347
349{
350 if ( !dataDefinedProperties().hasActiveProperties() )
351 {
352 return;
353 }
354
356 {
357 bool ok = false;
358 double value = mDataDefinedProperties.valueAsDouble( QgsPlot::DataDefinedProperty::MarginLeft, context.expressionContext(), margins.left(), &ok );
359
360 if ( ok )
361 {
362 margins.setLeft( value );
363 }
364 }
366 {
367 bool ok = false;
368 double value = mDataDefinedProperties.valueAsDouble( QgsPlot::DataDefinedProperty::MarginRight, context.expressionContext(), margins.right(), &ok );
369
370 if ( ok )
371 {
372 margins.setRight( value );
373 }
374 }
376 {
377 bool ok = false;
378 double value = mDataDefinedProperties.valueAsDouble( QgsPlot::DataDefinedProperty::MarginTop, context.expressionContext(), margins.top(), &ok );
379
380 if ( ok )
381 {
382 margins.setTop( value );
383 }
384 }
386 {
387 bool ok = false;
388 double value = mDataDefinedProperties.valueAsDouble( QgsPlot::DataDefinedProperty::MarginBottom, context.expressionContext(), margins.bottom(), &ok );
389
390 if ( ok )
391 {
392 margins.setBottom( value );
393 }
394 }
395}
396
398{
399 if ( !other )
400 {
401 return;
402 }
403
405 setSize( other->size() );
406 setMargins( other->margins() );
407}
408
409
410//
411// Qgs2DXyPlot
412//
413
415 : Qgs2DPlot()
416{
417 // setup default style
418 mChartBackgroundSymbol.reset( QgsPlotDefaultSettings::chartBackgroundSymbol() );
419 mChartBorderSymbol.reset( QgsPlotDefaultSettings::chartBorderSymbol() );
420}
421
422bool Qgs2DXyPlot::writeXml( QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context ) const
423{
424 Qgs2DPlot::writeXml( element, document, context );
425
426 element.setAttribute( u"minX"_s, qgsDoubleToString( mMinX ) );
427 element.setAttribute( u"maxX"_s, qgsDoubleToString( mMaxX ) );
428 element.setAttribute( u"minY"_s, qgsDoubleToString( mMinY ) );
429 element.setAttribute( u"maxY"_s, qgsDoubleToString( mMaxY ) );
430
431 element.setAttribute( u"flipAxes"_s, mFlipAxes ? "1"_L1 : "0"_L1 );
432
433 QDomElement xAxisElement = document.createElement( u"xAxis"_s );
434 mXAxis.writeXml( xAxisElement, document, context );
435 element.appendChild( xAxisElement );
436 QDomElement yAxisElement = document.createElement( u"yAxis"_s );
437 mYAxis.writeXml( yAxisElement, document, context );
438 element.appendChild( yAxisElement );
439
440 QDomElement backgroundElement = document.createElement( u"backgroundSymbol"_s );
441 backgroundElement.appendChild( QgsSymbolLayerUtils::saveSymbol( QString(), mChartBackgroundSymbol.get(), document, context ) );
442 element.appendChild( backgroundElement );
443 QDomElement borderElement = document.createElement( u"borderSymbol"_s );
444 borderElement.appendChild( QgsSymbolLayerUtils::saveSymbol( QString(), mChartBorderSymbol.get(), document, context ) );
445 element.appendChild( borderElement );
446
447 return true;
448}
449
450bool Qgs2DXyPlot::readXml( const QDomElement &element, const QgsReadWriteContext &context )
451{
452 Qgs2DPlot::readXml( element, context );
453
454 mFlipAxes = element.attribute( u"flipAxes"_s ).toInt() == 1;
455
456 mMinX = element.attribute( u"minX"_s ).toDouble();
457 mMaxX = element.attribute( u"maxX"_s ).toDouble();
458 mMinY = element.attribute( u"minY"_s ).toDouble();
459 mMaxY = element.attribute( u"maxY"_s ).toDouble();
460
461 const QDomElement xAxisElement = element.firstChildElement( u"xAxis"_s );
462 mXAxis.readXml( xAxisElement, context );
463 const QDomElement yAxisElement = element.firstChildElement( u"yAxis"_s );
464 mYAxis.readXml( yAxisElement, context );
465
466 const QDomElement backgroundElement = element.firstChildElement( u"backgroundSymbol"_s ).firstChildElement( u"symbol"_s );
467 mChartBackgroundSymbol = QgsSymbolLayerUtils::loadSymbol< QgsFillSymbol >( backgroundElement, context );
468 const QDomElement borderElement = element.firstChildElement( u"borderSymbol"_s ).firstChildElement( u"symbol"_s );
469 mChartBorderSymbol = QgsSymbolLayerUtils::loadSymbol< QgsFillSymbol >( borderElement, context );
470
471 return true;
472}
473
474void Qgs2DXyPlot::render( QgsRenderContext &context, QgsPlotRenderContext &plotContext, const QgsPlotData &plotData )
475{
476 QgsExpressionContextScope *plotScope = new QgsExpressionContextScope( u"plot"_s );
477 const QgsExpressionContextScopePopper scopePopper( context.expressionContext(), plotScope );
478
479 mChartBackgroundSymbol->startRender( context );
480 mChartBorderSymbol->startRender( context );
481
482 mXAxis.gridMinorSymbol()->startRender( context );
483 mYAxis.gridMinorSymbol()->startRender( context );
484 mXAxis.gridMajorSymbol()->startRender( context );
485 mYAxis.gridMajorSymbol()->startRender( context );
486
487 QgsMargins margins = mMargins;
489 double minX = mMinX;
490 double maxX = mMaxX;
491 double minY = mMinY;
492 double maxY = mMaxY;
493 double majorIntervalX = mXAxis.gridIntervalMajor();
494 double minorIntervalX = mXAxis.gridIntervalMinor();
495 double labelIntervalX = mXAxis.labelInterval();
496 double majorIntervalY = mYAxis.gridIntervalMajor();
497 double minorIntervalY = mYAxis.gridIntervalMinor();
498 double labelIntervalY = mYAxis.labelInterval();
499 applyDataDefinedProperties( context, minX, maxX, minY, maxY, majorIntervalX, minorIntervalX, labelIntervalX, majorIntervalY, minorIntervalY, labelIntervalY );
500
501 QgsPlotAxis *xAxis = nullptr;
502 QgsPlotAxis *yAxis = nullptr;
503 if ( mFlipAxes )
504 {
505 xAxis = &mYAxis;
506 yAxis = &mXAxis;
507 std::swap( minX, minY );
508 std::swap( maxX, maxY );
509 std::swap( majorIntervalX, majorIntervalY );
510 std::swap( minorIntervalX, minorIntervalY );
511 std::swap( labelIntervalX, labelIntervalY );
512 }
513 else
514 {
515 xAxis = &mXAxis;
516 yAxis = &mYAxis;
517 }
518
519 const double firstMinorXGrid = std::ceil( minX / minorIntervalX ) * minorIntervalX;
520 const double firstMajorXGrid = std::ceil( minX / majorIntervalX ) * majorIntervalX;
521 const double firstMinorYGrid = std::ceil( minY / minorIntervalY ) * minorIntervalY;
522 const double firstMajorYGrid = std::ceil( minY / majorIntervalY ) * majorIntervalY;
523 const double firstXLabel = labelIntervalX > 0 ? std::ceil( minX / labelIntervalX ) * labelIntervalX : 0;
524 const double firstYLabel = labelIntervalY > 0 ? std::ceil( minY / labelIntervalY ) * labelIntervalY : 0;
525
526 const QString xAxisSuffix = xAxis->labelSuffix();
527 const QString yAxisSuffix = yAxis->labelSuffix();
528
529 const QRectF plotArea = interiorPlotArea( context, plotContext, plotData );
530
531 const double xTolerance = minorIntervalX / 100000;
532 const double yTolerance = minorIntervalY / 100000;
533
534 QgsNumericFormatContext numericContext;
535
536 constexpr int MAX_OBJECTS = 1000;
537
538 // categories
539 const QStringList categories = plotData.categories();
540
541 // calculate text metrics
542 double maxYAxisLabelWidth = 0;
543 plotScope->addVariable( QgsExpressionContextScope::StaticVariable( u"plot_axis"_s, u"y"_s, true ) );
544 switch ( yAxis->type() )
545 {
547 if ( labelIntervalY > 0 )
548 {
549 int objectNumber = 0;
550 for ( double currentY = firstYLabel;; currentY += labelIntervalY, ++objectNumber )
551 {
552 const bool hasMoreLabels = objectNumber + 1 < MAX_OBJECTS && ( currentY + labelIntervalY <= maxY && !qgsDoubleNear( currentY + labelIntervalY, maxY, yTolerance ) );
553 plotScope->addVariable( QgsExpressionContextScope::StaticVariable( u"plot_axis_value"_s, currentY, true ) );
554 QString text = yAxis->numericFormat()->formatDouble( currentY, numericContext );
555 switch ( yAxis->labelSuffixPlacement() )
556 {
558 break;
559
561 text += yAxisSuffix;
562 break;
563
565 if ( currentY == firstYLabel )
566 text += yAxisSuffix;
567 break;
568
570 if ( !hasMoreLabels )
571 text += yAxisSuffix;
572 break;
573
575 if ( currentY == firstYLabel || !hasMoreLabels )
576 text += yAxisSuffix;
577 break;
578 }
579
580 maxYAxisLabelWidth = std::max( maxYAxisLabelWidth, QgsTextRenderer::textWidth( context, yAxis->textFormat(), { text } ) );
581 if ( !hasMoreLabels )
582 break;
583 }
584 }
585 break;
586
588 for ( int i = 0; i < categories.size(); i++ )
589 {
590 maxYAxisLabelWidth = std::max( maxYAxisLabelWidth, QgsTextRenderer::textWidth( context, yAxis->textFormat(), { categories.at( i ) } ) );
591 if ( i + 1 >= MAX_OBJECTS )
592 break;
593 }
594 break;
595 }
596
597 const double chartAreaLeft = plotArea.left();
598 const double chartAreaRight = plotArea.right();
599 const double chartAreaTop = plotArea.top();
600 const double chartAreaBottom = plotArea.bottom();
601
602 // chart background
603 mChartBackgroundSymbol->renderPolygon(
604 QPolygonF(
605 { QPointF( chartAreaLeft, chartAreaTop ),
606 QPointF( chartAreaRight, chartAreaTop ),
607 QPointF( chartAreaRight, chartAreaBottom ),
608 QPointF( chartAreaLeft, chartAreaBottom ),
609 QPointF( chartAreaLeft, chartAreaTop ) }
610 ),
611 nullptr,
612 nullptr,
613 context
614 );
615
616 const double xScale = ( chartAreaRight - chartAreaLeft ) / ( maxX - minX );
617 const double yScale = ( chartAreaBottom - chartAreaTop ) / ( maxY - minY );
618
619 // grid lines
620
621 // x
622 switch ( xAxis->type() )
623 {
625 {
626 plotScope->addVariable( QgsExpressionContextScope::StaticVariable( u"plot_axis"_s, u"x"_s, true ) );
627 double nextMajorXGrid = firstMajorXGrid;
628 int objectNumber = 0;
629 for ( double currentX = firstMinorXGrid; objectNumber < MAX_OBJECTS && ( currentX <= maxX && !qgsDoubleNear( currentX, maxX, xTolerance ) ); currentX += minorIntervalX, ++objectNumber )
630 {
631 bool isMinor = true;
632 if ( qgsDoubleNear( currentX, nextMajorXGrid, xTolerance ) )
633 {
634 isMinor = false;
635 nextMajorXGrid += majorIntervalX;
636 }
637
638 plotScope->addVariable( QgsExpressionContextScope::StaticVariable( u"plot_axis_value"_s, currentX, true ) );
639
640 QgsLineSymbol *currentGridSymbol = isMinor ? xAxis->gridMinorSymbol() : xAxis->gridMajorSymbol();
641 currentGridSymbol
642 ->renderPolyline( QPolygonF( QVector<QPointF> { QPointF( ( currentX - minX ) * xScale + chartAreaLeft, chartAreaBottom ), QPointF( ( currentX - minX ) * xScale + chartAreaLeft, chartAreaTop ) } ), nullptr, context );
643 }
644 break;
645 }
646
648 // No grid lines here, skipping
649 break;
650 }
651
652 // y
653 switch ( yAxis->type() )
654 {
656 {
657 plotScope->addVariable( QgsExpressionContextScope::StaticVariable( u"plot_axis"_s, u"y"_s, true ) );
658 double nextMajorYGrid = firstMajorYGrid;
659 int objectNumber = 0;
660 for ( double currentY = firstMinorYGrid; objectNumber < MAX_OBJECTS && ( currentY <= maxY && !qgsDoubleNear( currentY, maxY, yTolerance ) ); currentY += minorIntervalY, ++objectNumber )
661 {
662 bool isMinor = true;
663 if ( qgsDoubleNear( currentY, nextMajorYGrid, yTolerance ) )
664 {
665 isMinor = false;
666 nextMajorYGrid += majorIntervalY;
667 }
668
669 plotScope->addVariable( QgsExpressionContextScope::StaticVariable( u"plot_axis_value"_s, currentY, true ) );
670
671 QgsLineSymbol *currentGridSymbol = isMinor ? yAxis->gridMinorSymbol() : yAxis->gridMajorSymbol();
672 currentGridSymbol
673 ->renderPolyline( QPolygonF( QVector<QPointF> { QPointF( chartAreaLeft, chartAreaBottom - ( currentY - minY ) * yScale ), QPointF( chartAreaRight, chartAreaBottom - ( currentY - minY ) * yScale ) } ), nullptr, context );
674 }
675 break;
676 }
677
679 // No grid lines here, skipping
680 break;
681 }
682
683 // axis labels
684
685 // x
686 switch ( xAxis->type() )
687 {
689 {
690 plotScope->addVariable( QgsExpressionContextScope::StaticVariable( u"plot_axis"_s, u"x"_s, true ) );
691 if ( labelIntervalX > 0 )
692 {
693 int objectNumber = 0;
694 for ( double currentX = firstXLabel;; currentX += labelIntervalX, ++objectNumber )
695 {
696 const bool hasMoreLabels = objectNumber + 1 < MAX_OBJECTS && ( currentX + labelIntervalX <= maxX || qgsDoubleNear( currentX + labelIntervalX, maxX, xTolerance ) );
697 plotScope->addVariable( QgsExpressionContextScope::StaticVariable( u"plot_axis_value"_s, currentX, true ) );
698 QString text = xAxis->numericFormat()->formatDouble( currentX, numericContext );
699 switch ( xAxis->labelSuffixPlacement() )
700 {
702 break;
703
705 text += xAxisSuffix;
706 break;
707
709 if ( objectNumber == 0 )
710 text += xAxisSuffix;
711 break;
712
714 if ( !hasMoreLabels )
715 text += xAxisSuffix;
716 break;
717
719 if ( objectNumber == 0 || !hasMoreLabels )
720 text += xAxisSuffix;
721 break;
722 }
723
725 QPointF( ( currentX - minX ) * xScale + chartAreaLeft, mSize.height() - context.convertToPainterUnits( margins.bottom(), Qgis::RenderUnit::Millimeters ) ),
726 0,
728 { text },
729 context,
730 xAxis->textFormat()
731 );
732 if ( !hasMoreLabels )
733 break;
734 }
735 }
736 break;
737 }
738
740 {
741 plotScope->addVariable( QgsExpressionContextScope::StaticVariable( u"plot_axis"_s, u"x"_s, true ) );
742 const double categoryWidth = plotArea.width() / categories.size();
743 for ( int i = 0; i < categories.size(); i++ )
744 {
745 const double currentX = ( i * categoryWidth ) + categoryWidth / 2.0;
746 plotScope->addVariable( QgsExpressionContextScope::StaticVariable( u"plot_axis_value"_s, categories.at( i ), true ) );
748 QPointF( currentX + chartAreaLeft, mSize.height() - context.convertToPainterUnits( margins.bottom(), Qgis::RenderUnit::Millimeters ) ),
749 0,
751 { categories.at( i ) },
752 context,
753 xAxis->textFormat()
754 );
755 if ( i + 1 >= MAX_OBJECTS )
756 break;
757 }
758 break;
759 }
760 }
761
762 // y
763 switch ( yAxis->type() )
764 {
766 {
767 plotScope->addVariable( QgsExpressionContextScope::StaticVariable( u"plot_axis"_s, u"y"_s, true ) );
768 if ( labelIntervalY > 0 )
769 {
770 int objectNumber = 0;
771 for ( double currentY = firstYLabel;; currentY += labelIntervalY, ++objectNumber )
772 {
773 const bool hasMoreLabels = objectNumber + 1 < MAX_OBJECTS && ( currentY + labelIntervalY <= maxY || qgsDoubleNear( currentY + labelIntervalY, maxY, yTolerance ) );
774 plotScope->addVariable( QgsExpressionContextScope::StaticVariable( u"plot_axis_value"_s, currentY, true ) );
775 QString text = yAxis->numericFormat()->formatDouble( currentY, numericContext );
776 switch ( yAxis->labelSuffixPlacement() )
777 {
779 break;
780
782 text += yAxisSuffix;
783 break;
784
786 if ( objectNumber == 0 )
787 text += yAxisSuffix;
788 break;
789
791 if ( !hasMoreLabels )
792 text += yAxisSuffix;
793 break;
794
796 if ( objectNumber == 0 || !hasMoreLabels )
797 text += yAxisSuffix;
798 break;
799 }
800
801 const double height = QgsTextRenderer::textHeight( context, yAxis->textFormat(), { text } );
803 QPointF( maxYAxisLabelWidth + context.convertToPainterUnits( margins.left(), Qgis::RenderUnit::Millimeters ), chartAreaBottom - ( currentY - minY ) * yScale + height / 2 ),
804 0,
806 { text },
807 context,
808 yAxis->textFormat(),
809 false
810 );
811 if ( !hasMoreLabels )
812 break;
813 }
814 }
815 break;
816 }
817
819 {
820 plotScope->addVariable( QgsExpressionContextScope::StaticVariable( u"plot_axis"_s, u"y"_s, true ) );
821 const double categoryHeight = plotArea.height() / categories.size();
822 for ( int i = 0; i < categories.size(); i++ )
823 {
824 const double currentY = ( i * categoryHeight ) + categoryHeight / 2.0;
825 plotScope->addVariable( QgsExpressionContextScope::StaticVariable( u"plot_axis_value"_s, categories.at( i ), true ) );
826 const double height = QgsTextRenderer::textHeight( context, yAxis->textFormat(), { categories.at( i ) } );
828 QPointF( maxYAxisLabelWidth + context.convertToPainterUnits( margins.left(), Qgis::RenderUnit::Millimeters ), chartAreaBottom - currentY + height / 2 ),
829 0,
831 { categories.at( i ) },
832 context,
833 yAxis->textFormat(),
834 false
835 );
836
837 if ( i + 1 >= MAX_OBJECTS )
838 break;
839 }
840 break;
841 }
842 }
843
844 // give subclasses a chance to draw their content
845 renderContent( context, plotContext, plotArea, plotData );
846
847 // border
848 mChartBorderSymbol->renderPolygon(
849 QPolygonF(
850 { QPointF( chartAreaLeft, chartAreaTop ),
851 QPointF( chartAreaRight, chartAreaTop ),
852 QPointF( chartAreaRight, chartAreaBottom ),
853 QPointF( chartAreaLeft, chartAreaBottom ),
854 QPointF( chartAreaLeft, chartAreaTop ) }
855 ),
856 nullptr,
857 nullptr,
858 context
859 );
860
861 mChartBackgroundSymbol->stopRender( context );
862 mChartBorderSymbol->stopRender( context );
863 xAxis->gridMinorSymbol()->stopRender( context );
864 yAxis->gridMinorSymbol()->stopRender( context );
865 xAxis->gridMajorSymbol()->stopRender( context );
866 yAxis->gridMajorSymbol()->stopRender( context );
867}
868
869Qgs2DXyPlot::~Qgs2DXyPlot() = default;
870
872{
873 // categories
874 const QStringList categories = plotData.categories();
875
876 double minX = mMinX;
877 double maxX = mMaxX;
878 double minY = mMinY;
879 double maxY = mMaxY;
880 double majorIntervalX = mXAxis.gridIntervalMajor();
881 double minorIntervalX = mXAxis.gridIntervalMinor();
882 double labelIntervalX = mXAxis.labelInterval();
883 double majorIntervalY = mYAxis.gridIntervalMajor();
884 double minorIntervalY = mYAxis.gridIntervalMinor();
885 double labelIntervalY = mYAxis.labelInterval();
886 applyDataDefinedProperties( context, minX, maxX, minY, maxY, majorIntervalX, minorIntervalX, labelIntervalX, majorIntervalY, minorIntervalY, labelIntervalY );
887
888 const QgsPlotAxis *xAxis = mFlipAxes ? &mYAxis : &mXAxis;
889 const QgsPlotAxis *yAxis = mFlipAxes ? &mXAxis : &mYAxis;
890 if ( mFlipAxes )
891 {
892 std::swap( minX, minY );
893 std::swap( maxX, maxY );
894 std::swap( majorIntervalX, majorIntervalY );
895 std::swap( minorIntervalX, minorIntervalY );
896 std::swap( labelIntervalX, labelIntervalY );
897 }
898
899 QgsExpressionContextScope *plotScope = new QgsExpressionContextScope( u"plot"_s );
900 const QgsExpressionContextScopePopper scopePopper( context.expressionContext(), plotScope );
901
902 const double firstMinorYGrid = std::ceil( minY / minorIntervalY ) * minorIntervalY;
903 const double firstXLabel = labelIntervalX > 0 ? std::ceil( minX / labelIntervalX ) * labelIntervalX : 0;
904
905 const QString xAxisSuffix = xAxis->labelSuffix();
906 const QString yAxisSuffix = yAxis->labelSuffix();
907 const double yAxisSuffixWidth = yAxisSuffix.isEmpty() ? 0 : QgsTextRenderer::textWidth( context, yAxis->textFormat(), { yAxisSuffix } );
908
909 QgsNumericFormatContext numericContext;
910
911 const double xTolerance = minorIntervalX / 100000;
912 const double yTolerance = minorIntervalX / 100000;
913
914 constexpr int MAX_LABELS = 1000;
915
916 // calculate text metrics
917 int labelNumber = 0;
918 double maxXAxisLabelHeight = 0;
919 plotScope->addVariable( QgsExpressionContextScope::StaticVariable( u"plot_axis"_s, u"x"_s, true ) );
920 switch ( xAxis->type() )
921 {
923 if ( labelIntervalX > 0 )
924 {
925 for ( double currentX = firstXLabel;; currentX += labelIntervalX, labelNumber++ )
926 {
927 const bool hasMoreLabels = labelNumber + 1 < MAX_LABELS && ( currentX + labelIntervalX <= maxX || qgsDoubleNear( currentX + labelIntervalX, maxX, xTolerance ) );
928
929 plotScope->addVariable( QgsExpressionContextScope::StaticVariable( u"plot_axis_value"_s, currentX, true ) );
930 QString text = xAxis->numericFormat()->formatDouble( currentX, numericContext );
931 switch ( xAxis->labelSuffixPlacement() )
932 {
934 break;
935
937 text += xAxisSuffix;
938 break;
939
941 if ( labelNumber == 0 )
942 text += xAxisSuffix;
943 break;
944
946 if ( !hasMoreLabels )
947 text += xAxisSuffix;
948 break;
949
951 if ( labelNumber == 0 || !hasMoreLabels )
952 text += xAxisSuffix;
953 break;
954 }
955 maxXAxisLabelHeight = std::max( maxXAxisLabelHeight, QgsTextRenderer::textHeight( context, xAxis->textFormat(), { text } ) );
956 if ( !hasMoreLabels )
957 break;
958 }
959 }
960 break;
961
963 for ( int i = 0; i < categories.size(); i++ )
964 {
965 maxXAxisLabelHeight = std::max( maxXAxisLabelHeight, QgsTextRenderer::textHeight( context, xAxis->textFormat(), { categories.at( i ) } ) );
966 if ( i + 1 >= MAX_LABELS )
967 break;
968 }
969 break;
970 }
971
972 double maxYAxisLabelWidth = 0;
973 labelNumber = 0;
974 plotScope->addVariable( QgsExpressionContextScope::StaticVariable( u"plot_axis"_s, u"y"_s, true ) );
975 switch ( yAxis->type() )
976 {
978 for ( double currentY = firstMinorYGrid;; currentY += minorIntervalY, labelNumber++ )
979 {
980 const bool hasMoreLabels = labelNumber + 1 < MAX_LABELS && ( currentY + minorIntervalY <= maxY || qgsDoubleNear( currentY + minorIntervalY, maxY, yTolerance ) );
981 plotScope->addVariable( QgsExpressionContextScope::StaticVariable( u"plot_axis_value"_s, currentY, true ) );
982 const QString text = yAxis->numericFormat()->formatDouble( currentY, numericContext );
983 double thisLabelWidth = QgsTextRenderer::textWidth( context, yAxis->textFormat(), { text } );
984 if ( yAxisSuffixWidth > 0 )
985 {
986 switch ( yAxis->labelSuffixPlacement() )
987 {
989 break;
990
992 thisLabelWidth += yAxisSuffixWidth;
993 break;
994
996 if ( labelNumber == 0 )
997 thisLabelWidth += yAxisSuffixWidth;
998 break;
999
1001 if ( !hasMoreLabels )
1002 thisLabelWidth += yAxisSuffixWidth;
1003 break;
1004
1006 if ( labelNumber == 0 || !hasMoreLabels )
1007 thisLabelWidth += yAxisSuffixWidth;
1008 break;
1009 }
1010 }
1011 maxYAxisLabelWidth = std::max( maxYAxisLabelWidth, thisLabelWidth );
1012 if ( !hasMoreLabels )
1013 break;
1014 }
1015 break;
1016
1018 for ( int i = 0; i < categories.size(); i++ )
1019 {
1020 maxYAxisLabelWidth = std::max( maxYAxisLabelWidth, QgsTextRenderer::textWidth( context, yAxis->textFormat(), { categories.at( i ) } ) );
1021 if ( i + 1 >= MAX_LABELS )
1022 break;
1023 }
1024 break;
1025 }
1026
1027 const double leftTextSize = maxYAxisLabelWidth + context.convertToPainterUnits( 1, Qgis::RenderUnit::Millimeters );
1028 const double rightTextSize = 0;
1029 const double bottomTextSize = maxXAxisLabelHeight + context.convertToPainterUnits( 0.5, Qgis::RenderUnit::Millimeters );
1030 const double topTextSize = 0;
1031
1032 QgsMargins margins = mMargins;
1034 const double leftMargin = context.convertToPainterUnits( margins.left(), Qgis::RenderUnit::Millimeters ) + leftTextSize;
1035 const double rightMargin = context.convertToPainterUnits( margins.right(), Qgis::RenderUnit::Millimeters ) + rightTextSize;
1036 const double topMargin = context.convertToPainterUnits( margins.top(), Qgis::RenderUnit::Millimeters ) + topTextSize;
1037 const double bottomMargin = context.convertToPainterUnits( margins.bottom(), Qgis::RenderUnit::Millimeters ) + bottomTextSize;
1038
1039 return QRectF( leftMargin, topMargin, mSize.width() - rightMargin - leftMargin, mSize.height() - bottomMargin - topMargin );
1040}
1041
1043{
1044 if ( !mSize.isValid() )
1045 return;
1046
1047 // aim for about 40% coverage of label text to available space
1048 constexpr double IDEAL_WIDTH = 0.4;
1049 constexpr double TOLERANCE = 0.04;
1050 constexpr int MAX_LABELS = 1000;
1051
1052 QgsMargins margins = mMargins;
1054 const double leftMargin = context.convertToPainterUnits( margins.left(), Qgis::RenderUnit::Millimeters );
1055 const double rightMargin = context.convertToPainterUnits( margins.right(), Qgis::RenderUnit::Millimeters );
1056 const double topMargin = context.convertToPainterUnits( margins.top(), Qgis::RenderUnit::Millimeters );
1057 const double bottomMargin = context.convertToPainterUnits( margins.bottom(), Qgis::RenderUnit::Millimeters );
1058
1059 QgsNumericFormatContext numericContext;
1060
1061 auto refineIntervalForAxis =
1062 [&]( double axisMinimum, double axisMaximum, const std::function< double( double ) > &sizeForLabel, double availableSize, double idealSizePercent, double sizeTolerancePercent, double &labelInterval, double &majorInterval, double &minorInterval ) {
1063 auto roundBase10 = []( double value ) -> double { return std::pow( 10, std::floor( std::log10( value ) ) ); };
1064
1065 // if the current interval is good enough, don't change it!
1066 double totalSize = 0;
1067 int initialLabelCount = 0;
1068 {
1069 const double firstLabelPos = std::ceil( axisMinimum / labelInterval ) * labelInterval;
1070
1071 for ( double currentPos = firstLabelPos; initialLabelCount <= MAX_LABELS && currentPos <= axisMaximum; currentPos += labelInterval, ++initialLabelCount )
1072 {
1073 totalSize += sizeForLabel( currentPos );
1074 }
1075 }
1076
1077 // we consider the current interval as "good enough" if it results in somewhere between 20-60% label text coverage over the size
1078 if ( initialLabelCount >= MAX_LABELS || ( totalSize / availableSize < ( idealSizePercent - sizeTolerancePercent ) ) || ( totalSize / availableSize > ( idealSizePercent + sizeTolerancePercent ) ) )
1079 {
1080 // we start with trying to fit 30 labels in and then raise the interval till we're happy
1081 int numberLabelsInitial = std::floor( availableSize / 30 );
1082
1083 double labelIntervalTest = ( axisMaximum - axisMinimum ) / numberLabelsInitial;
1084 double baseValue = roundBase10( labelIntervalTest );
1085 double candidate = baseValue;
1086 int currentMultiplier = 1;
1087
1088 int numberLabels = 0;
1089 while ( true )
1090 {
1091 const double firstLabelPosition = std::ceil( axisMinimum / candidate ) * candidate;
1092 double totalSize = 0;
1093 numberLabels = 0;
1094 for ( double currentPos = firstLabelPosition; currentPos <= axisMaximum; currentPos += candidate )
1095 {
1096 totalSize += sizeForLabel( currentPos );
1097 numberLabels += 1;
1098
1099 if ( numberLabels > MAX_LABELS ) // avoid hangs if candidate size is very small
1100 break;
1101 }
1102
1103 if ( numberLabels <= MAX_LABELS && totalSize <= availableSize * idealSizePercent )
1104 break;
1105
1106 if ( currentMultiplier == 1 )
1107 currentMultiplier = 2;
1108 else if ( currentMultiplier == 2 )
1109 currentMultiplier = 5;
1110 else if ( currentMultiplier == 5 )
1111 {
1112 baseValue *= 10;
1113 currentMultiplier = 1;
1114 }
1115
1116 candidate = baseValue * currentMultiplier;
1117 }
1118 labelInterval = candidate;
1119 if ( numberLabels < 10 )
1120 {
1121 minorInterval = labelInterval / 2;
1122 majorInterval = minorInterval * 4;
1123 }
1124 else
1125 {
1126 minorInterval = labelInterval;
1127 majorInterval = minorInterval * 5;
1128 }
1129 }
1130 };
1131
1132 double minX = mMinX;
1133 double maxX = mMaxX;
1134 double minY = mMinY;
1135 double maxY = mMaxY;
1136 double majorIntervalX = mXAxis.gridIntervalMajor();
1137 double minorIntervalX = mXAxis.gridIntervalMinor();
1138 double labelIntervalX = mXAxis.labelInterval();
1139 double majorIntervalY = mYAxis.gridIntervalMajor();
1140 double minorIntervalY = mYAxis.gridIntervalMinor();
1141 double labelIntervalY = mYAxis.labelInterval();
1142 applyDataDefinedProperties( context, minX, maxX, minY, maxY, majorIntervalX, minorIntervalX, labelIntervalX, majorIntervalY, minorIntervalY, labelIntervalY );
1143
1144 {
1145 const double availableSize = mFlipAxes ? mSize.height() - topMargin - bottomMargin : mSize.width() - leftMargin - rightMargin;
1146 const QString suffixX = mXAxis.labelSuffix();
1147 const double suffixWidth = !suffixX.isEmpty() ? QgsTextRenderer::textWidth( context, mXAxis.textFormat(), { suffixX } ) : 0;
1148 refineIntervalForAxis(
1149 minX,
1150 maxX,
1151 [this, &context, suffixWidth, &numericContext]( double position ) -> double {
1152 const QString text = mXAxis.numericFormat()->formatDouble( position, numericContext );
1153 // this isn't accurate, as we're always considering the suffix to be present... but it's too tricky to actually consider
1154 // the suffix placement!
1155 return QgsTextRenderer::textWidth( context, mXAxis.textFormat(), { text } ) + suffixWidth;
1156 },
1157 availableSize,
1158 IDEAL_WIDTH,
1159 TOLERANCE,
1160 labelIntervalX,
1161 majorIntervalX,
1162 minorIntervalX
1163 );
1164 mXAxis.setLabelInterval( labelIntervalX );
1165 mXAxis.setGridIntervalMajor( majorIntervalX );
1166 mXAxis.setGridIntervalMinor( minorIntervalX );
1167 }
1168
1169 {
1170 const double availableSize = mFlipAxes ? mSize.width() - leftMargin - rightMargin : mSize.height() - topMargin - bottomMargin;
1171 const QString suffixY = mYAxis.labelSuffix();
1172 refineIntervalForAxis(
1173 minY,
1174 maxY,
1175 [this, &context, suffixY, &numericContext]( double position ) -> double {
1176 const QString text = mYAxis.numericFormat()->formatDouble( position, numericContext );
1177 // this isn't accurate, as we're always considering the suffix to be present... but it's too tricky to actually consider
1178 // the suffix placement!
1179 return QgsTextRenderer::textHeight( context, mYAxis.textFormat(), { text + suffixY } );
1180 },
1181 availableSize,
1182 IDEAL_WIDTH,
1183 TOLERANCE,
1184 labelIntervalY,
1185 majorIntervalY,
1186 minorIntervalY
1187 );
1188 mYAxis.setLabelInterval( labelIntervalY );
1189 mYAxis.setGridIntervalMajor( majorIntervalY );
1190 mYAxis.setGridIntervalMinor( minorIntervalY );
1191 }
1192}
1193
1195{
1196 return mChartBackgroundSymbol.get();
1197}
1198
1200{
1201 return mChartBackgroundSymbol.get();
1202}
1203
1205{
1206 mChartBackgroundSymbol.reset( symbol );
1207}
1208
1210{
1211 return mChartBorderSymbol.get();
1212}
1213
1215{
1216 return mChartBorderSymbol.get();
1217}
1218
1220{
1221 mChartBorderSymbol.reset( symbol );
1222}
1223
1225{
1226 mFlipAxes = flipAxes;
1227}
1228
1230 QgsRenderContext &context,
1231 double &minX,
1232 double &maxX,
1233 double &minY,
1234 double &maxY,
1235 double &majorIntervalX,
1236 double &minorIntervalX,
1237 double &labelIntervalX,
1238 double &majorIntervalY,
1239 double &minorIntervalY,
1240 double &labelIntervalY
1241) const
1242{
1243 if ( !dataDefinedProperties().hasActiveProperties() )
1244 {
1245 return;
1246 }
1247
1249 {
1250 bool ok = false;
1251 double value = mDataDefinedProperties.valueAsDouble( QgsPlot::DataDefinedProperty::XAxisMinimum, context.expressionContext(), minX, &ok );
1252
1253 if ( ok )
1254 {
1255 minX = value;
1256 }
1257 }
1259 {
1260 bool ok = false;
1261 double value = mDataDefinedProperties.valueAsDouble( QgsPlot::DataDefinedProperty::XAxisMaximum, context.expressionContext(), maxX, &ok );
1262
1263 if ( ok )
1264 {
1265 maxX = value;
1266 }
1267 }
1269 {
1270 bool ok = false;
1271 double value = mDataDefinedProperties.valueAsDouble( QgsPlot::DataDefinedProperty::YAxisMinimum, context.expressionContext(), minY, &ok );
1272
1273 if ( ok )
1274 {
1275 minY = value;
1276 }
1277 }
1279 {
1280 bool ok = false;
1281 double value = mDataDefinedProperties.valueAsDouble( QgsPlot::DataDefinedProperty::YAxisMaximum, context.expressionContext(), maxY, &ok );
1282
1283 if ( ok )
1284 {
1285 maxY = value;
1286 }
1287 }
1289 {
1290 bool ok = false;
1291 double value = mDataDefinedProperties.valueAsDouble( QgsPlot::DataDefinedProperty::XAxisMajorInterval, context.expressionContext(), majorIntervalX, &ok );
1292
1293 if ( ok )
1294 {
1295 majorIntervalX = value;
1296 }
1297 }
1299 {
1300 bool ok = false;
1301 double value = mDataDefinedProperties.valueAsDouble( QgsPlot::DataDefinedProperty::XAxisMinorInterval, context.expressionContext(), minorIntervalX, &ok );
1302
1303 if ( ok )
1304 {
1305 minorIntervalX = value;
1306 }
1307 }
1309 {
1310 bool ok = false;
1311 double value = mDataDefinedProperties.valueAsDouble( QgsPlot::DataDefinedProperty::XAxisLabelInterval, context.expressionContext(), labelIntervalX, &ok );
1312
1313 if ( ok )
1314 {
1315 labelIntervalX = value;
1316 }
1317 }
1319 {
1320 bool ok = false;
1321 double value = mDataDefinedProperties.valueAsDouble( QgsPlot::DataDefinedProperty::YAxisMajorInterval, context.expressionContext(), majorIntervalY, &ok );
1322
1323 if ( ok )
1324 {
1325 majorIntervalY = value;
1326 }
1327 }
1329 {
1330 bool ok = false;
1331 double value = mDataDefinedProperties.valueAsDouble( QgsPlot::DataDefinedProperty::YAxisMinorInterval, context.expressionContext(), minorIntervalY, &ok );
1332
1333 if ( ok )
1334 {
1335 minorIntervalY = value;
1336 }
1337 }
1339 {
1340 bool ok = false;
1341 double value = mDataDefinedProperties.valueAsDouble( QgsPlot::DataDefinedProperty::YAxisLabelInterval, context.expressionContext(), labelIntervalY, &ok );
1342
1343 if ( ok )
1344 {
1345 labelIntervalY = value;
1346 }
1347 }
1348}
1349
1351{
1352 if ( !other )
1353 {
1354 return;
1355 }
1356
1358 setXMinimum( other->xMinimum() );
1359 setXMaximum( other->xMaximum() );
1360 setYMinimum( other->yMinimum() );
1361 setYMaximum( other->yMaximum() );
1362 setFlipAxes( other->flipAxes() );
1363
1364 QgsPlotAxis::copyProperties( other->xAxis(), mXAxis );
1365 QgsPlotAxis::copyProperties( other->yAxis(), mYAxis );
1366
1367 if ( other->chartBackgroundSymbol() )
1368 {
1370 }
1371 if ( other->chartBorderSymbol() )
1372 {
1374 }
1375}
1376
1377
1378//
1379// QgsPlotDefaultSettings
1380//
1381
1386
1388{
1389 auto gridMajor = std::make_unique< QgsSimpleLineSymbolLayer >( QColor( 20, 20, 20, 150 ), 0.1 );
1390 gridMajor->setPenCapStyle( Qt::FlatCap );
1391 return new QgsLineSymbol( QgsSymbolLayerList( { gridMajor.release() } ) );
1392}
1393
1395{
1396 auto gridMinor = std::make_unique< QgsSimpleLineSymbolLayer >( QColor( 20, 20, 20, 50 ), 0.1 );
1397 gridMinor->setPenCapStyle( Qt::FlatCap );
1398 return new QgsLineSymbol( QgsSymbolLayerList( { gridMinor.release() } ) );
1399}
1400
1402{
1403 auto chartFill = std::make_unique< QgsSimpleFillSymbolLayer >( QColor( 255, 255, 255 ) );
1404 return new QgsFillSymbol( QgsSymbolLayerList( { chartFill.release() } ) );
1405}
1406
1408{
1409 auto chartBorder = std::make_unique< QgsSimpleLineSymbolLayer >( QColor( 20, 20, 20 ), 0.1 );
1410 return new QgsFillSymbol( QgsSymbolLayerList( { chartBorder.release() } ) );
1411}
1412
1414{
1415 auto chartMarker = std::make_unique< QgsSimpleMarkerSymbolLayer>( Qgis::MarkerShape::Circle, 1.8, 0.0, DEFAULT_SCALE_METHOD, QColor( 89, 150, 50 ) );
1416 return new QgsMarkerSymbol( QgsSymbolLayerList( { chartMarker.release() } ) );
1417}
1418
1420{
1421 auto chartLine = std::make_unique< QgsSimpleLineSymbolLayer>( QColor( 89, 150, 50, 100 ), 0.6 );
1422 return new QgsLineSymbol( QgsSymbolLayerList( { chartLine.release() } ) );
1423}
1424
1426{
1427 auto chartFill = std::make_unique< QgsSimpleFillSymbolLayer>( QColor( 89, 150, 50 ) );
1428 return new QgsFillSymbol( QgsSymbolLayerList( { chartFill.release() } ) );
1429}
1430
1432{
1433 auto chartFill = std::make_unique< QgsSimpleFillSymbolLayer>( QColor( 150, 150, 150 ) );
1434 return new QgsFillSymbol( QgsSymbolLayerList( { chartFill.release() } ) );
1435}
1436
1438{
1439 return new QgsPresetSchemeColorRamp(
1440 { QColor( 89, 150, 50 ), QColor( 228, 26, 28 ), QColor( 55, 126, 184 ), QColor( 152, 78, 163 ), QColor( 255, 127, 0 ), QColor( 166, 86, 40 ), QColor( 247, 129, 191 ), QColor( 153, 153, 153 ) }
1441 );
1442}
1443
1448
1449//
1450// QgsPlotData
1451//
1452
1457
1459 : mCategories( other.mCategories )
1460{
1461 for ( QgsAbstractPlotSeries *series : other.mSeries )
1462 {
1463 addSeries( series->clone() );
1464 }
1465}
1466
1468 : mSeries( std::move( other.mSeries ) )
1469 , mCategories( std::move( other.mCategories ) )
1470{}
1471
1473{
1474 if ( this != &other )
1475 {
1476 clearSeries();
1477
1478 mCategories = other.mCategories;
1479 for ( QgsAbstractPlotSeries *series : other.mSeries )
1480 {
1481 addSeries( series->clone() );
1482 }
1483 }
1484 return *this;
1485}
1486
1488{
1489 if ( this != &other )
1490 {
1491 clearSeries();
1492
1493 mCategories = std::move( other.mCategories );
1494 mSeries = std::move( other.mSeries );
1495 }
1496 return *this;
1497}
1498
1499QList<QgsAbstractPlotSeries *> QgsPlotData::series() const
1500{
1501 return mSeries;
1502}
1503
1505{
1506 if ( !mSeries.contains( series ) )
1507 {
1508 mSeries << series;
1509 }
1510}
1511
1513{
1514 qDeleteAll( mSeries );
1515 mSeries.clear();
1516}
1517
1518QStringList QgsPlotData::categories() const
1519{
1520 return mCategories;
1521}
1522
1523void QgsPlotData::setCategories( const QStringList &categories )
1524{
1525 mCategories = categories;
1526}
1527
1528//
1529// QgsAbstractPlotSeries
1530//
1531
1533{
1534 return mName;
1535}
1536
1538{
1539 mName = name;
1540}
1541
1542//
1543// QgsXyPlotSeries
1544//
1545
1546QList<std::pair<double, double>> QgsXyPlotSeries::data() const
1547{
1548 return mData;
1549}
1550
1551void QgsXyPlotSeries::setData( const QList<std::pair<double, double>> &data )
1552{
1553 mData = data;
1554}
1555
1556void QgsXyPlotSeries::append( double x, double y )
1557{
1558 mData << std::make_pair( x, y );
1559}
1560
1562{
1563 mData.clear();
1564}
1565
1567{
1568 QgsXyPlotSeries *series = new QgsXyPlotSeries();
1569 series->setName( name() );
1570 series->setData( mData );
1571 return series;
1572}
PlotAxisSuffixPlacement
Placement options for suffixes in the labels for axis of plots.
Definition qgis.h:3432
@ FirstAndLastLabels
Place suffix after the first and last label values only.
Definition qgis.h:3437
@ EveryLabel
Place suffix after every value label.
Definition qgis.h:3434
@ FirstLabel
Place suffix after the first label value only.
Definition qgis.h:3435
@ LastLabel
Place suffix after the last label value only.
Definition qgis.h:3436
@ NoLabels
Do not place suffixes.
Definition qgis.h:3433
@ Circle
Circle.
Definition qgis.h:3203
PlotAxisType
Plots axis types.
Definition qgis.h:3448
@ Categorical
The axis represents categories.
Definition qgis.h:3450
@ Interval
The axis represents a range of values.
Definition qgis.h:3449
@ Millimeters
Millimeters.
Definition qgis.h:5341
@ Center
Center align.
Definition qgis.h:3045
bool writeXml(QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context) const override
Writes the plot's properties into an XML element.
Definition qgsplot.cpp:281
virtual QRectF interiorPlotArea(QgsRenderContext &context, QgsPlotRenderContext &plotContext, const QgsPlotData &plotData=QgsPlotData()) const
Returns the area of the plot which corresponds to the actual plot content (excluding all titles and o...
Definition qgsplot.cpp:325
void applyDataDefinedProperties(QgsRenderContext &context, QgsMargins &margins) const
Applies 2D plot data-defined properties.
Definition qgsplot.cpp:348
virtual void renderContent(QgsRenderContext &context, QgsPlotRenderContext &plotContext, const QRectF &plotArea, const QgsPlotData &plotData=QgsPlotData())
Renders the plot content.
Definition qgsplot.cpp:310
void setSize(QSizeF size)
Sets the overall size of the plot (including titles and over components which sit outside the plot ar...
Definition qgsplot.cpp:320
const QgsMargins & margins() const
Returns the margins of the plot area (in millimeters).
Definition qgsplot.cpp:338
~Qgs2DPlot() override
void copyCommonProperties(const Qgs2DPlot *other)
Copies all Qgs2DPlot-level properties (size, margins, data-defined properties) from other to this plo...
Definition qgsplot.cpp:397
Qgs2DPlot()
Constructor for Qgs2DPlot.
Definition qgsplot.cpp:277
bool readXml(const QDomElement &element, const QgsReadWriteContext &context) override
Reads the plot's properties from an XML element.
Definition qgsplot.cpp:290
virtual void render(QgsRenderContext &context, QgsPlotRenderContext &plotContext, const QgsPlotData &plotData=QgsPlotData())
Renders the plot.
Definition qgsplot.cpp:299
QSizeF size() const
Returns the overall size of the plot (in millimeters) (including titles and other components which si...
Definition qgsplot.cpp:315
void setMargins(const QgsMargins &margins)
Sets the margins of the plot area (in millimeters).
Definition qgsplot.cpp:343
void copyCommonProperties(const Qgs2DXyPlot *other)
Copies all Qgs2DXyPlot-level properties (axis ranges, axes, flip state, background and border symbols...
Definition qgsplot.cpp:1350
void setXMaximum(double maximum)
Sets the maximum value of the x axis.
Definition qgsplot.h:762
double yMaximum() const
Returns the maximum value of the y axis.
Definition qgsplot.h:769
void setYMinimum(double minimum)
Sets the minimum value of the y axis.
Definition qgsplot.h:748
double xMinimum() const
Returns the minimum value of the x axis.
Definition qgsplot.h:727
bool readXml(const QDomElement &element, const QgsReadWriteContext &context) override
Reads the plot's properties from an XML element.
Definition qgsplot.cpp:450
void setChartBackgroundSymbol(QgsFillSymbol *symbol)
Sets the fill symbol used to render the background of the chart.
Definition qgsplot.cpp:1204
void setFlipAxes(bool flipAxes)
Sets whether the X and Y axes are flipped.
Definition qgsplot.cpp:1224
void applyDataDefinedProperties(QgsRenderContext &context, double &minX, double &maxX, double &minY, double &maxY, double &majorIntervalX, double &minorIntervalX, double &labelIntervalX, double &majorIntervalY, double &minorIntervalY, double &labelIntervalY) const
Applies 2D XY plot data-defined properties.
Definition qgsplot.cpp:1229
QgsPlotAxis & yAxis()
Returns a reference to the plot's y axis.
Definition qgsplot.h:797
void setChartBorderSymbol(QgsFillSymbol *symbol)
Sets the symbol used to render the border of the chart.
Definition qgsplot.cpp:1219
bool flipAxes() const
Returns whether the X and Y axes are flipped.
Definition qgsplot.h:856
~Qgs2DXyPlot() override
void render(QgsRenderContext &context, QgsPlotRenderContext &plotContext, const QgsPlotData &plotData=QgsPlotData()) override
Renders the plot.
Definition qgsplot.cpp:474
QgsFillSymbol * chartBackgroundSymbol()
Returns the fill symbol used to render the background of the chart.
Definition qgsplot.cpp:1194
void setXMinimum(double minimum)
Sets the minimum value of the x axis.
Definition qgsplot.h:734
void calculateOptimisedIntervals(QgsRenderContext &context, QgsPlotRenderContext &plotContext)
Automatically sets the grid and label intervals to optimal values for display in the given render con...
Definition qgsplot.cpp:1042
QgsPlotAxis & xAxis()
Returns a reference to the plot's x axis.
Definition qgsplot.h:783
Qgs2DXyPlot()
Constructor for Qgs2DXyPlot.
Definition qgsplot.cpp:414
QgsFillSymbol * chartBorderSymbol()
Returns the symbol used to render the border of the chart.
Definition qgsplot.cpp:1209
double yMinimum() const
Returns the minimum value of the y axis.
Definition qgsplot.h:741
QRectF interiorPlotArea(QgsRenderContext &context, QgsPlotRenderContext &plotContext, const QgsPlotData &plotData=QgsPlotData()) const override
Returns the area of the plot which corresponds to the actual plot content (excluding all titles and o...
Definition qgsplot.cpp:871
void setYMaximum(double maximum)
Sets the maximum value of the y axis.
Definition qgsplot.h:776
bool writeXml(QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context) const override
Writes the plot's properties into an XML element.
Definition qgsplot.cpp:422
double xMaximum() const
Returns the maximum value of the x axis.
Definition qgsplot.h:755
An abstract class used to encapsulate the data for a plot series.
Definition qgsplot.h:210
void setName(const QString &name)
Sets the series' name.
Definition qgsplot.cpp:1537
QString name() const
Returns the series' name.
Definition qgsplot.cpp:1532
QgsAbstractPlotSeries()=default
static QgsNumericFormatRegistry * numericFormatRegistry()
Gets the registry of available numeric formats.
A numeric formatter which returns a simple text representation of a value.
Abstract base class for color ramps.
RAII class to pop scope from an expression context on destruction.
Single scope for storing variables and functions for use within a QgsExpressionContext.
void addVariable(const QgsExpressionContextScope::StaticVariable &variable)
Adds a variable into the context scope.
A fill symbol type, for rendering Polygon and MultiPolygon geometries.
QgsFillSymbol * clone() const override
Returns a deep copy of this symbol.
A line symbol type, for rendering LineString and MultiLineString geometries.
QgsLineSymbol * clone() const override
Returns a deep copy of this symbol.
void renderPolyline(const QPolygonF &points, const QgsFeature *f, QgsRenderContext &context, int layer=-1, bool selected=false)
Renders the symbol along the line joining points, using the given render context.
Defines the four margins of a rectangle.
Definition qgsmargins.h:40
double top() const
Returns the top margin.
Definition qgsmargins.h:76
static QgsMargins fromString(const QString &string)
Returns a QgsMargins object decoded from a string, or a null QgsMargins if the string could not be in...
double right() const
Returns the right margin.
Definition qgsmargins.h:82
double bottom() const
Returns the bottom margin.
Definition qgsmargins.h:88
double left() const
Returns the left margin.
Definition qgsmargins.h:70
A marker symbol type, for rendering Point and MultiPoint geometries.
A context for numeric formats.
Abstract base class for numeric formatters, which allow for formatting a numeric value for display.
virtual QgsNumericFormat * clone() const =0
Clones the format, returning a new object.
Encapsulates the properties of a plot axis.
Definition qgsplot.h:354
QgsLineSymbol * gridMinorSymbol()
Returns the line symbol used to render the minor lines in the axis grid.
Definition qgsplot.cpp:230
bool readXml(const QDomElement &element, const QgsReadWriteContext &context)
Reads the axis' properties from an XML element.
Definition qgsplot.cpp:161
static void copyProperties(const QgsPlotAxis &source, QgsPlotAxis &destination)
Copies all properties from source axis to destination axis.
Definition qgsplot.cpp:255
void setGridMajorSymbol(QgsLineSymbol *symbol)
Sets the symbol used to render the major lines in the axis grid.
Definition qgsplot.cpp:225
double gridIntervalMinor() const
Returns the interval of minor grid lines for the axis.
Definition qgsplot.h:389
double gridIntervalMajor() const
Returns the interval of major grid lines for the axis.
Definition qgsplot.h:403
void setType(Qgis::PlotAxisType type)
Sets the axis type.
Definition qgsplot.cpp:129
void setNumericFormat(QgsNumericFormat *format)
Sets the numeric format used for the axis labels.
Definition qgsplot.cpp:190
QgsTextFormat textFormat() const
Returns the text format used for the axis labels.
Definition qgsplot.cpp:245
void setGridIntervalMajor(double interval)
Sets the interval of major grid lines for the axis.
Definition qgsplot.h:410
void setLabelSuffixPlacement(Qgis::PlotAxisSuffixPlacement placement)
Sets the placement for the axis label suffixes.
Definition qgsplot.cpp:210
void setGridIntervalMinor(double interval)
Sets the interval of minor grid lines for the axis.
Definition qgsplot.h:396
void setLabelInterval(double interval)
Sets the interval of labels for the axis.
Definition qgsplot.h:424
Qgis::PlotAxisType type() const
Returns the axis type.
Definition qgsplot.cpp:124
double labelInterval() const
Returns the interval of labels for the axis.
Definition qgsplot.h:417
QgsLineSymbol * gridMajorSymbol()
Returns the line symbol used to render the major lines in the axis grid.
Definition qgsplot.cpp:215
void setLabelSuffix(const QString &suffix)
Sets the axis label suffix.
Definition qgsplot.cpp:200
void setTextFormat(const QgsTextFormat &format)
Sets the text format used for the axis labels.
Definition qgsplot.cpp:250
bool writeXml(QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context) const
Writes the axis' properties into an XML element.
Definition qgsplot.cpp:134
void setGridMinorSymbol(QgsLineSymbol *symbol)
Sets the symbol used to render the minor lines in the axis grid.
Definition qgsplot.cpp:240
QgsNumericFormat * numericFormat() const
Returns the numeric format used for the axis labels.
Definition qgsplot.cpp:185
Qgis::PlotAxisSuffixPlacement labelSuffixPlacement() const
Returns the placement for the axis label suffixes.
Definition qgsplot.cpp:205
QString labelSuffix() const
Returns the axis label suffix, or an empty string if no label suffix is to be used.
Definition qgsplot.cpp:195
Encapsulates one or more plot series.
Definition qgsplot.h:303
QgsPlotData()=default
QStringList categories() const
Returns the name of the series' categories.
Definition qgsplot.cpp:1518
QgsPlotData & operator=(const QgsPlotData &other)
Definition qgsplot.cpp:1472
void clearSeries()
Clears all series from the plot data.
Definition qgsplot.cpp:1512
QList< QgsAbstractPlotSeries * > series() const
Returns the list of series forming the plot data.
Definition qgsplot.cpp:1499
void setCategories(const QStringList &categories)
Sets the name of the series' categories.
Definition qgsplot.cpp:1523
void addSeries(QgsAbstractPlotSeries *series)
Adds a series to the plot data.
Definition qgsplot.cpp:1504
static QgsFillSymbol * chartBorderSymbol()
Returns the default fill symbol to use for the chart area border.
Definition qgsplot.cpp:1407
static QgsNumericFormat * pieChartNumericFormat()
Returns the default color ramp to use for pie charts.
Definition qgsplot.cpp:1444
static QgsNumericFormat * axisLabelNumericFormat()
Returns the default numeric format to use for plot axis labels.
Definition qgsplot.cpp:1382
static QgsColorRamp * pieChartColorRamp()
Returns the default color ramp to use for pie charts.
Definition qgsplot.cpp:1437
static QgsFillSymbol * barChartFillSymbol()
Returns the default fill symbol to use for bar charts.
Definition qgsplot.cpp:1425
static QgsLineSymbol * lineChartLineSymbol()
Returns the default line symbol to use for line charts.
Definition qgsplot.cpp:1419
static QgsLineSymbol * axisGridMinorSymbol()
Returns the default line symbol to use for axis minor grid lines.
Definition qgsplot.cpp:1394
static QgsFillSymbol * pieChartFillSymbol()
Returns the default fill symbol to use for pie charts.
Definition qgsplot.cpp:1431
static QgsMarkerSymbol * lineChartMarkerSymbol()
Returns the default marker symbol to use for line charts.
Definition qgsplot.cpp:1413
static QgsFillSymbol * chartBackgroundSymbol()
Returns the default fill symbol to use for the chart area background fill.
Definition qgsplot.cpp:1401
static QgsLineSymbol * axisGridMajorSymbol()
Returns the default line symbol to use for axis major grid lines.
Definition qgsplot.cpp:1387
Contains information about the context of a plot rendering operation.
Definition qgsplot.h:191
QgsPlot()=default
void setDataDefinedProperties(const QgsPropertyCollection &collection)
Sets the plot's property collection, used for data defined overrides.
Definition qgsplot.h:151
static const QgsPropertiesDefinition & propertyDefinitions()
Returns the plot property definitions.
Definition qgsplot.cpp:95
virtual ~QgsPlot()
QgsPropertyCollection & dataDefinedProperties()
Returns a reference to the plot's property collection, used for data defined overrides.
Definition qgsplot.h:136
virtual QString type() const
Returns the plot's type.
Definition qgsplot.h:121
QgsPropertyCollection mDataDefinedProperties
Definition qgsplot.h:176
virtual void initFromPlot(const QgsPlot *plot)
Initializes properties of this plot from an existing plot, transferring all applicable settings.
Definition qgsplot.cpp:101
@ YAxisMinorInterval
Minor grid line interval for Y axis.
Definition qgsplot.h:105
@ YAxisLabelInterval
Label interval for Y axis.
Definition qgsplot.h:106
@ XAxisMajorInterval
Major grid line interval for X axis.
Definition qgsplot.h:101
@ XAxisMaximum
Maximum X axis value.
Definition qgsplot.h:108
@ XAxisMinimum
Minimum X axis value.
Definition qgsplot.h:107
@ XAxisLabelInterval
Label interval for X axis.
Definition qgsplot.h:103
@ MarginBottom
Bottom margin.
Definition qgsplot.h:100
@ XAxisMinorInterval
Minor grid line interval for X axis.
Definition qgsplot.h:102
@ YAxisMajorInterval
Major grid line interval for Y axis.
Definition qgsplot.h:104
@ YAxisMaximum
Maximum Y axis value.
Definition qgsplot.h:110
@ MarginLeft
Left margin.
Definition qgsplot.h:97
@ MarginRight
Right margin.
Definition qgsplot.h:99
@ YAxisMinimum
Minimum Y axis value.
Definition qgsplot.h:109
virtual bool readXml(const QDomElement &element, const QgsReadWriteContext &context)
Reads the plot's properties from an XML element.
Definition qgsplot.cpp:56
virtual bool writeXml(QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context) const
Writes the plot's properties into an XML element.
Definition qgsplot.cpp:45
A scheme based color ramp consisting of a list of predefined colors.
Definition for a property.
Definition qgsproperty.h:47
@ Double
Double value (including negative values).
Definition qgsproperty.h:56
@ 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 convertToPainterUnits(double size, Qgis::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale(), Qgis::RenderSubcomponentProperty property=Qgis::RenderSubcomponentProperty::Generic) const
Converts a size from the specified units to painter units (pixels).
QgsExpressionContext & expressionContext()
Gets the expression context.
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.
Container for all settings relating to text rendering.
static double textWidth(const QgsRenderContext &context, const QgsTextFormat &format, const QStringList &textLines, QFontMetricsF *fontMetrics=nullptr)
Returns the width of a text based on a given format.
static void drawText(const QRectF &rect, double rotation, Qgis::TextHorizontalAlignment alignment, const QStringList &textLines, QgsRenderContext &context, const QgsTextFormat &format, bool drawAsOutlines=true, Qgis::TextVerticalAlignment vAlignment=Qgis::TextVerticalAlignment::Top, Qgis::TextRendererFlags flags=Qgis::TextRendererFlags(), Qgis::TextLayoutMode mode=Qgis::TextLayoutMode::Rectangle)
Draws text within a rectangle using the specified settings.
static double textHeight(const QgsRenderContext &context, const QgsTextFormat &format, const QStringList &textLines, Qgis::TextLayoutMode mode=Qgis::TextLayoutMode::Point, QFontMetricsF *fontMetrics=nullptr, Qgis::TextRendererFlags flags=Qgis::TextRendererFlags(), double maxLineWidth=0)
Returns the height of a text based on a given format.
void setData(const QList< std::pair< double, double > > &data)
Sets the series' list of XY pairs of double.
Definition qgsplot.cpp:1551
QgsAbstractPlotSeries * clone() const override
Clones the series.
Definition qgsplot.cpp:1566
QgsXyPlotSeries()=default
void append(double x, double y)
Appends a pair of X/Y double values to the series.
Definition qgsplot.cpp:1556
void clear()
Clears the series' data.
Definition qgsplot.cpp:1561
QList< std::pair< double, double > > data() const
Returns the series' list of XY pairs of double.
Definition qgsplot.cpp:1546
T qgsEnumKeyToValue(const QString &key, const T &defaultValue, bool tryValueAsKey=true, bool *returnOk=nullptr)
Returns the value corresponding to the given key of an enum.
Definition qgis.h:7176
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition qgis.h:6893
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition qgis.h:7157
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6975
constexpr int TOLERANCE
QMap< int, QgsPropertyDefinition > QgsPropertiesDefinition
Definition of available properties.
QList< QgsSymbolLayer * > QgsSymbolLayerList
Definition qgssymbol.h:30
#define DEFAULT_SCALE_METHOD
Single variable definition for use within a QgsExpressionContextScope.