QGIS API Documentation 3.99.0-Master (c22de0620c0)
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 "qgssymbol.h"
22#include "qgssymbollayer.h"
23#include "qgssymbollayerutils.h"
25
26#include <QString>
27
28using namespace Qt::StringLiterals;
29
34
35void QgsBarChartPlot::renderContent( QgsRenderContext &context, QgsPlotRenderContext &, const QRectF &plotArea, const QgsPlotData &plotData )
36{
37 if ( mFillSymbols.empty() )
38 {
39 return;
40 }
41
42 const QList<QgsAbstractPlotSeries *> seriesList = plotData.series();
43 if ( seriesList.isEmpty() )
44 {
45 return;
46 }
47
48 const QStringList categories = plotData.categories();
49 switch ( xAxis().type() )
50 {
52 if ( categories.isEmpty() )
53 {
54 return;
55 }
56 break;
57
59 break;
60 }
61
62 QgsExpressionContextScope *chartScope = new QgsExpressionContextScope( u"chart"_s );
63 const QgsExpressionContextScopePopper scopePopper( context.expressionContext(), chartScope );
64
65 context.painter()->save();
66 context.painter()->setClipRect( plotArea );
67
68 double minX = xMinimum();
69 double maxX = xMaximum();
70 double minY = yMinimum();
71 double maxY = yMaximum();
72 double majorIntervalX = xAxis().gridIntervalMajor();
73 double minorIntervalX = xAxis().gridIntervalMinor();
74 double labelIntervalX = xAxis().labelInterval();
75 double majorIntervalY = yAxis().gridIntervalMajor();
76 double minorIntervalY = yAxis().gridIntervalMinor();
77 double labelIntervalY = yAxis().labelInterval();
78 Qgs2DXyPlot::applyDataDefinedProperties( context, minX, maxX, minY, maxY, majorIntervalX, minorIntervalX, labelIntervalX, majorIntervalY, minorIntervalY, labelIntervalY );
79
80 double xScale = 0.0;
81 double yScale = 0.0;
82 double categoriesWidth = 0.0;
83 double valuesWidth = 0.0;
84 double barsWidth = 0.0;
85 double barWidth = 0.0;
86 if ( flipAxes() )
87 {
88 xScale = plotArea.height() / ( maxX - minX );
89 yScale = plotArea.width() / ( maxY - minY );
90 categoriesWidth = plotArea.height() / static_cast<double>( categories.size() );
91 valuesWidth = plotArea.height() * ( minorIntervalX / ( maxX - minY ) );
92 barsWidth = xAxis().type() == Qgis::PlotAxisType::Categorical ? categoriesWidth / 2 : valuesWidth / 2;
93 barWidth = barsWidth / seriesList.size();
94 }
95 else
96 {
97 xScale = plotArea.width() / ( maxX - minX );
98 yScale = plotArea.height() / ( maxY - minY );
99 categoriesWidth = plotArea.width() / static_cast<double>( categories.size() );
100 valuesWidth = plotArea.width() * ( minorIntervalX / ( maxX - minX ) );
101 barsWidth = xAxis().type() == Qgis::PlotAxisType::Categorical ? categoriesWidth / 2 : valuesWidth / 2;
102 barWidth = barsWidth / seriesList.size();
103 }
104
105 int seriesIndex = 0;
106 for ( const QgsAbstractPlotSeries *series : seriesList )
107 {
108 QgsFillSymbol *symbol = fillSymbolAt( seriesIndex % mFillSymbols.size() );
109 if ( !symbol )
110 {
111 continue;
112 }
113 symbol->startRender( context );
114
115 chartScope->addVariable( QgsExpressionContextScope::StaticVariable( u"chart_series_name"_s, series->name(), true ) );
116
117 const double barStartAdjustment = -( barsWidth / 2 ) + barWidth * seriesIndex;
118 if ( const QgsXyPlotSeries *xySeries = dynamic_cast<const QgsXyPlotSeries *>( series ) )
119 {
120 const QList<std::pair<double, double>> data = xySeries->data();
121 for ( const std::pair<double, double> &pair : data )
122 {
123 double x = 0;
124 switch ( xAxis().type() )
125 {
127 if ( pair.first < 0 || pair.first >= categories.size() )
128 {
129 continue;
130 }
131 x = ( categoriesWidth * pair.first ) + ( categoriesWidth / 2 ) + barStartAdjustment;
132 chartScope->addVariable( QgsExpressionContextScope::StaticVariable( u"chart_category"_s, categories[pair.first], true ) );
133 break;
134
136 x = ( pair.first - minX ) * xScale + barStartAdjustment;
137 break;
138 }
139
140 const double y = ( pair.second - minY ) * yScale;
141 const double zero = ( 0.0 - minY ) * yScale;
142 QPoint topLeft;
143 QPoint bottomRight;
144 if ( flipAxes() )
145 {
146 topLeft = QPoint( plotArea.x() + zero,
147 plotArea.bottom() - x - barWidth );
148 bottomRight = QPoint( plotArea.x() + y,
149 plotArea.bottom() - x );
150 }
151 else
152 {
153 topLeft = QPoint( plotArea.left() + x,
154 plotArea.y() + plotArea.height() - y );
155 bottomRight = QPoint( plotArea.left() + x + barWidth,
156 plotArea.y() + plotArea.height() - zero );
157 }
158
159 chartScope->addVariable( QgsExpressionContextScope::StaticVariable( u"chart_value"_s, pair.second, true ) );
160 symbol->renderPolygon( QPolygonF( QRectF( topLeft, bottomRight ) ), nullptr, nullptr, context );
161 }
162 }
163
164 symbol->stopRender( context );
165 seriesIndex++;
166 }
167
168 context.painter()->restore();
169}
170
172{
173 if ( index < 0 || index >= static_cast<int>( mFillSymbols.size() ) )
174 {
175 return nullptr;
176 }
177
178 return mFillSymbols[index].get();
179}
180
182{
183 if ( index < 0 )
184 {
185 return;
186 }
187
188 if ( index + 1 >= static_cast<int>( mFillSymbols.size() ) )
189 {
190 mFillSymbols.resize( index + 1 );
191 }
192
193 mFillSymbols[index].reset( symbol );
194}
195
196bool QgsBarChartPlot::writeXml( QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context ) const
197{
198 Qgs2DXyPlot::writeXml( element, document, context );
199
200 QDomElement fillSymbolsElement = document.createElement( u"fillSymbols"_s );
201 for ( int i = 0; i < static_cast<int>( mFillSymbols.size() ); i++ )
202 {
203 QDomElement fillSymbolElement = document.createElement( u"fillSymbol"_s );
204 fillSymbolElement.setAttribute( u"index"_s, QString::number( i ) );
205 if ( mFillSymbols[i] )
206 {
207 fillSymbolElement.appendChild( QgsSymbolLayerUtils::saveSymbol( QString(), mFillSymbols[i].get(), document, context ) );
208 }
209 fillSymbolsElement.appendChild( fillSymbolElement );
210 }
211 element.appendChild( fillSymbolsElement );
212
213 return true;
214}
215
216bool QgsBarChartPlot::readXml( const QDomElement &element, const QgsReadWriteContext &context )
217{
218 Qgs2DXyPlot::readXml( element, context );
219
220 const QDomNodeList fillSymbolsList = element.firstChildElement( u"fillSymbols"_s ).childNodes();
221 for ( int i = 0; i < fillSymbolsList.count(); i++ )
222 {
223 const QDomElement fillSymbolElement = fillSymbolsList.at( i ).toElement();
224 const int index = fillSymbolElement.attribute( u"index"_s, u"-1"_s ).toInt();
225 if ( index >= 0 )
226 {
227 if ( fillSymbolElement.hasChildNodes() )
228 {
229 const QDomElement symbolElement = fillSymbolElement.firstChildElement( u"symbol"_s );
230 setFillSymbolAt( index, QgsSymbolLayerUtils::loadSymbol< QgsFillSymbol >( symbolElement, context ).release() );
231 }
232 else
233 {
234 setFillSymbolAt( index, nullptr );
235 }
236 }
237 }
238
239 return true;
240}
241
246
248{
249 QgsBarChartPlot *chart = dynamic_cast<QgsBarChartPlot *>( plot );
250 if ( !chart )
251 {
252 return nullptr;
253 }
254
255 return new QgsVectorLayerXyPlotDataGatherer( chart->xAxis().type() );
256}
@ Categorical
The axis represents categories.
Definition qgis.h:3422
@ Interval
The axis represents a range of values.
Definition qgis.h:3421
double yMaximum() const
Returns the maximum value of the y axis.
Definition qgsplot.h:744
double xMinimum() const
Returns the minimum value of the x axis.
Definition qgsplot.h:702
bool readXml(const QDomElement &element, const QgsReadWriteContext &context) override
Reads the plot's properties from an XML element.
Definition qgsplot.cpp:396
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:1133
QgsPlotAxis & yAxis()
Returns a reference to the plot's y axis.
Definition qgsplot.h:772
bool flipAxes() const
Returns whether the X and Y axes are flipped.
Definition qgsplot.h:816
QgsPlotAxis & xAxis()
Returns a reference to the plot's x axis.
Definition qgsplot.h:758
double yMinimum() const
Returns the minimum value of the y axis.
Definition qgsplot.h:716
bool writeXml(QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context) const override
Writes the plot's properties into an XML element.
Definition qgsplot.cpp:368
double xMaximum() const
Returns the maximum value of the x axis.
Definition qgsplot.h:730
An abstract class used to encapsulate the data for a plot series.
Definition qgsplot.h:204
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.
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:107
double labelInterval() const
Returns the interval of labels for the axis.
Definition qgsplot.h:417
Encapsulates one or more plot series.
Definition qgsplot.h:300
QStringList categories() const
Returns the name of the series' categories.
Definition qgsplot.cpp:1381
QList< QgsAbstractPlotSeries * > series() const
Returns the list of series forming the plot data.
Definition qgsplot.cpp:1362
static QgsFillSymbol * barChartFillSymbol()
Returns the default fill symbol to use for bar charts.
Definition qgsplot.cpp:1289
Contains information about the context of a plot rendering operation.
Definition qgsplot.h:184
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.
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:258
Single variable definition for use within a QgsExpressionContextScope.