QGIS API Documentation 4.3.0-Master (b46232e4f9a)
Loading...
Searching...
No Matches
qgsbarchartplot.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsbarchartplot.cpp
3 -------------------
4 begin : June 2025
5 copyright : (C) 2025 by Mathieu
6 email : mathieu at opengis dot ch
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 "qgsbarchartplot.h"
19
21#include "qgspainting.h"
22#include "qgssymbol.h"
23#include "qgssymbollayer.h"
24#include "qgssymbollayerutils.h"
26
27#include <QString>
28
29using namespace Qt::StringLiterals;
30
35
36void QgsBarChartPlot::renderContent( QgsRenderContext &context, QgsPlotRenderContext &, const QRectF &plotArea, const QgsPlotData &plotData )
37{
38 if ( mFillSymbols.empty() )
39 {
40 return;
41 }
42
43 const QList<QgsAbstractPlotSeries *> seriesList = plotData.series();
44 if ( seriesList.isEmpty() )
45 {
46 return;
47 }
48
49 const QStringList categories = plotData.categories();
50 switch ( xAxis().type() )
51 {
53 if ( categories.isEmpty() )
54 {
55 return;
56 }
57 break;
58
60 break;
61 }
62
63 QgsExpressionContextScope *chartScope = new QgsExpressionContextScope( u"chart"_s );
64 const QgsExpressionContextScopePopper scopePopper( context.expressionContext(), chartScope );
65
66 QgsScopedQPainterState painterState( context.painter() );
67 context.painter()->setClipRect( plotArea );
68
69 double minX = xMinimum();
70 double maxX = xMaximum();
71 double minY = yMinimum();
72 double maxY = yMaximum();
73 double majorIntervalX = xAxis().gridIntervalMajor();
74 double minorIntervalX = xAxis().gridIntervalMinor();
75 double labelIntervalX = xAxis().labelInterval();
76 double majorIntervalY = yAxis().gridIntervalMajor();
77 double minorIntervalY = yAxis().gridIntervalMinor();
78 double labelIntervalY = yAxis().labelInterval();
79 Qgs2DXyPlot::applyDataDefinedProperties( context, minX, maxX, minY, maxY, majorIntervalX, minorIntervalX, labelIntervalX, majorIntervalY, minorIntervalY, labelIntervalY );
80
81 double xScale = 0.0;
82 double yScale = 0.0;
83 double categoriesWidth = 0.0;
84 double valuesWidth = 0.0;
85 double barsWidth = 0.0;
86 double barWidth = 0.0;
87 if ( flipAxes() )
88 {
89 xScale = plotArea.height() / ( maxX - minX );
90 yScale = plotArea.width() / ( maxY - minY );
91 categoriesWidth = plotArea.height() / static_cast<double>( categories.size() );
92 valuesWidth = plotArea.height() * ( minorIntervalX / ( maxX - minY ) );
93 barsWidth = xAxis().type() == Qgis::PlotAxisType::Categorical ? categoriesWidth / 2 : valuesWidth / 2;
94 barWidth = barsWidth / seriesList.size();
95 }
96 else
97 {
98 xScale = plotArea.width() / ( maxX - minX );
99 yScale = plotArea.height() / ( maxY - minY );
100 categoriesWidth = plotArea.width() / static_cast<double>( categories.size() );
101 valuesWidth = plotArea.width() * ( minorIntervalX / ( maxX - minX ) );
102 barsWidth = xAxis().type() == Qgis::PlotAxisType::Categorical ? categoriesWidth / 2 : valuesWidth / 2;
103 barWidth = barsWidth / seriesList.size();
104 }
105
106 int seriesIndex = 0;
107 for ( const QgsAbstractPlotSeries *series : seriesList )
108 {
109 QgsFillSymbol *symbol = fillSymbolAt( seriesIndex % mFillSymbols.size() );
110 if ( !symbol )
111 {
112 continue;
113 }
114 symbol->startRender( context );
115
116 chartScope->addVariable( QgsExpressionContextScope::StaticVariable( u"chart_series_name"_s, series->name(), true ) );
117
118 const double barStartAdjustment = -( barsWidth / 2 ) + barWidth * seriesIndex;
119 if ( const QgsXyPlotSeries *xySeries = dynamic_cast<const QgsXyPlotSeries *>( series ) )
120 {
121 const QList<std::pair<double, double>> data = xySeries->data();
122 for ( const std::pair<double, double> &pair : data )
123 {
124 double x = 0;
125 switch ( xAxis().type() )
126 {
128 if ( pair.first < 0 || pair.first >= categories.size() )
129 {
130 continue;
131 }
132 x = ( categoriesWidth * pair.first ) + ( categoriesWidth / 2 ) + barStartAdjustment;
133 chartScope->addVariable( QgsExpressionContextScope::StaticVariable( u"chart_category"_s, categories[pair.first], true ) );
134 break;
135
137 x = ( pair.first - minX ) * xScale + barStartAdjustment;
138 break;
139 }
140
141 const double y = ( pair.second - minY ) * yScale;
142 const double zero = ( 0.0 - minY ) * yScale;
143 QPoint topLeft;
144 QPoint bottomRight;
145 if ( flipAxes() )
146 {
147 topLeft = QPoint( plotArea.x() + zero, plotArea.bottom() - x - barWidth );
148 bottomRight = QPoint( plotArea.x() + y, plotArea.bottom() - x );
149 }
150 else
151 {
152 topLeft = QPoint( plotArea.left() + x, plotArea.y() + plotArea.height() - y );
153 bottomRight = QPoint( plotArea.left() + x + barWidth, plotArea.y() + plotArea.height() - zero );
154 }
155
156 chartScope->addVariable( QgsExpressionContextScope::StaticVariable( u"chart_value"_s, pair.second, true ) );
157 symbol->renderPolygon( QPolygonF( QRectF( topLeft, bottomRight ) ), nullptr, nullptr, context );
158 }
159 }
160
161 symbol->stopRender( context );
162 seriesIndex++;
163 }
164}
165
167{
168 if ( index < 0 || index >= static_cast<int>( mFillSymbols.size() ) )
169 {
170 return nullptr;
171 }
172
173 return mFillSymbols[index].get();
174}
175
177{
178 if ( index < 0 )
179 {
180 return;
181 }
182
183 if ( index + 1 >= static_cast<int>( mFillSymbols.size() ) )
184 {
185 mFillSymbols.resize( index + 1 );
186 }
187
188 mFillSymbols[index].reset( symbol );
189}
190
191bool QgsBarChartPlot::writeXml( QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context ) const
192{
193 Qgs2DXyPlot::writeXml( element, document, context );
194
195 QDomElement fillSymbolsElement = document.createElement( u"fillSymbols"_s );
196 for ( int i = 0; i < static_cast<int>( mFillSymbols.size() ); i++ )
197 {
198 QDomElement fillSymbolElement = document.createElement( u"fillSymbol"_s );
199 fillSymbolElement.setAttribute( u"index"_s, QString::number( i ) );
200 if ( mFillSymbols[i] )
201 {
202 fillSymbolElement.appendChild( QgsSymbolLayerUtils::saveSymbol( QString(), mFillSymbols[i].get(), document, context ) );
203 }
204 fillSymbolsElement.appendChild( fillSymbolElement );
205 }
206 element.appendChild( fillSymbolsElement );
207
208 return true;
209}
210
211bool QgsBarChartPlot::readXml( const QDomElement &element, const QgsReadWriteContext &context )
212{
213 Qgs2DXyPlot::readXml( element, context );
214
215 const QDomNodeList fillSymbolsList = element.firstChildElement( u"fillSymbols"_s ).childNodes();
216 for ( int i = 0; i < fillSymbolsList.count(); i++ )
217 {
218 const QDomElement fillSymbolElement = fillSymbolsList.at( i ).toElement();
219 const int index = fillSymbolElement.attribute( u"index"_s, u"-1"_s ).toInt();
220 if ( index >= 0 )
221 {
222 if ( fillSymbolElement.hasChildNodes() )
223 {
224 const QDomElement symbolElement = fillSymbolElement.firstChildElement( u"symbol"_s );
225 setFillSymbolAt( index, QgsSymbolLayerUtils::loadSymbol< QgsFillSymbol >( symbolElement, context ).release() );
226 }
227 else
228 {
229 setFillSymbolAt( index, nullptr );
230 }
231 }
232 }
233
234 return true;
235}
236
241
243{
244 QgsBarChartPlot *chart = dynamic_cast<QgsBarChartPlot *>( plot );
245 if ( !chart )
246 {
247 return nullptr;
248 }
249
250 return new QgsVectorLayerXyPlotDataGatherer( chart->xAxis().type() );
251}
252
254{
255 if ( !plot )
256 {
257 return;
258 }
259
260 if ( const Qgs2DXyPlot *plotXy = dynamic_cast<const Qgs2DXyPlot *>( plot ) )
261 {
263 }
264 else if ( const Qgs2DPlot *plotPie = dynamic_cast<const Qgs2DPlot *>( plot ) )
265 {
267 }
268
269 if ( const QgsBarChartPlot *barChartPlot = dynamic_cast<const QgsBarChartPlot *>( plot ) )
270 {
271 for ( int idx = 0; idx < barChartPlot->fillSymbolCount(); idx++ )
272 {
273 setFillSymbolAt( idx, barChartPlot->fillSymbolAt( idx )->clone() );
274 }
275 }
276}
@ Categorical
The axis represents categories.
Definition qgis.h:3574
@ Interval
The axis represents a range of values.
Definition qgis.h:3573
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
void copyCommonProperties(const Qgs2DXyPlot *other)
Copies all Qgs2DXyPlot-level properties (axis ranges, axes, flip state, background and border symbols...
Definition qgsplot.cpp:1350
double yMaximum() const
Returns the maximum value of the y axis.
Definition qgsplot.h:769
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 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
bool flipAxes() const
Returns whether the X and Y axes are flipped.
Definition qgsplot.h:856
QgsPlotAxis & xAxis()
Returns a reference to the plot's x axis.
Definition qgsplot.h:783
Qgs2DXyPlot()
Constructor for Qgs2DXyPlot.
Definition qgsplot.cpp:414
double yMinimum() const
Returns the minimum value of the y axis.
Definition qgsplot.h:741
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
QString type() const override
Returns the plot's type.
void setFillSymbolAt(int index, QgsFillSymbol *symbol)
Sets the fill symbol to use for the series with matching index.
void renderContent(QgsRenderContext &context, QgsPlotRenderContext &plotContext, const QRectF &plotArea, const QgsPlotData &plotData=QgsPlotData()) override
Renders the plot content.
bool readXml(const QDomElement &element, const QgsReadWriteContext &context) override
Reads the plot's properties from an XML element.
static QgsVectorLayerAbstractPlotDataGatherer * createDataGatherer(QgsPlot *plot)
Returns a new data gatherer for a given bar chart plot.
QgsFillSymbol * fillSymbolAt(int index) const
Returns the fill symbol for the series with matching index.
void initFromPlot(const QgsPlot *plot) override
Initializes properties of this plot from an existing plot, transferring all applicable settings.
bool writeXml(QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context) const override
Writes the plot's properties into an XML element.
static QgsBarChartPlot * create()
Returns a new bar chart.
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.
void renderPolygon(const QPolygonF &points, const QVector< QPolygonF > *rings, const QgsFeature *f, QgsRenderContext &context, int layer=-1, bool selected=false)
Renders the symbol using the given render context.
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
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
Encapsulates one or more plot series.
Definition qgsplot.h:303
QStringList categories() const
Returns the name of the series' categories.
Definition qgsplot.cpp:1518
QList< QgsAbstractPlotSeries * > series() const
Returns the list of series forming the plot data.
Definition qgsplot.cpp:1499
static QgsFillSymbol * barChartFillSymbol()
Returns the default fill symbol to use for bar charts.
Definition qgsplot.cpp:1425
Contains information about the context of a plot rendering operation.
Definition qgsplot.h:191
QgsPlot()=default
A container for the context for various read/write operations on objects.
Contains information about the context of a rendering operation.
QPainter * painter()
Returns the destination QPainter for the render operation.
QgsExpressionContext & expressionContext()
Gets the expression context.
Scoped object for saving and restoring a QPainter object's state.
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.
void stopRender(QgsRenderContext &context)
Ends the rendering process.
void startRender(QgsRenderContext &context, const QgsFields &fields=QgsFields())
Begins the rendering process for the symbol.
An abstract vector layer plot data gatherer base class.
An vector layer plot data gatherer class for XY series.
Encapsulates the data for an XY plot series.
Definition qgsplot.h:263
Single variable definition for use within a QgsExpressionContextScope.