QGIS API Documentation 4.0.0-Norrköping (1ddcee3d0e4)
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, plotArea.bottom() - x - barWidth );
147 bottomRight = QPoint( plotArea.x() + y, plotArea.bottom() - x );
148 }
149 else
150 {
151 topLeft = QPoint( plotArea.left() + x, plotArea.y() + plotArea.height() - y );
152 bottomRight = QPoint( plotArea.left() + x + barWidth, plotArea.y() + plotArea.height() - zero );
153 }
154
155 chartScope->addVariable( QgsExpressionContextScope::StaticVariable( u"chart_value"_s, pair.second, true ) );
156 symbol->renderPolygon( QPolygonF( QRectF( topLeft, bottomRight ) ), nullptr, nullptr, context );
157 }
158 }
159
160 symbol->stopRender( context );
161 seriesIndex++;
162 }
163
164 context.painter()->restore();
165}
166
168{
169 if ( index < 0 || index >= static_cast<int>( mFillSymbols.size() ) )
170 {
171 return nullptr;
172 }
173
174 return mFillSymbols[index].get();
175}
176
178{
179 if ( index < 0 )
180 {
181 return;
182 }
183
184 if ( index + 1 >= static_cast<int>( mFillSymbols.size() ) )
185 {
186 mFillSymbols.resize( index + 1 );
187 }
188
189 mFillSymbols[index].reset( symbol );
190}
191
192bool QgsBarChartPlot::writeXml( QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context ) const
193{
194 Qgs2DXyPlot::writeXml( element, document, context );
195
196 QDomElement fillSymbolsElement = document.createElement( u"fillSymbols"_s );
197 for ( int i = 0; i < static_cast<int>( mFillSymbols.size() ); i++ )
198 {
199 QDomElement fillSymbolElement = document.createElement( u"fillSymbol"_s );
200 fillSymbolElement.setAttribute( u"index"_s, QString::number( i ) );
201 if ( mFillSymbols[i] )
202 {
203 fillSymbolElement.appendChild( QgsSymbolLayerUtils::saveSymbol( QString(), mFillSymbols[i].get(), document, context ) );
204 }
205 fillSymbolsElement.appendChild( fillSymbolElement );
206 }
207 element.appendChild( fillSymbolsElement );
208
209 return true;
210}
211
212bool QgsBarChartPlot::readXml( const QDomElement &element, const QgsReadWriteContext &context )
213{
214 Qgs2DXyPlot::readXml( element, context );
215
216 const QDomNodeList fillSymbolsList = element.firstChildElement( u"fillSymbols"_s ).childNodes();
217 for ( int i = 0; i < fillSymbolsList.count(); i++ )
218 {
219 const QDomElement fillSymbolElement = fillSymbolsList.at( i ).toElement();
220 const int index = fillSymbolElement.attribute( u"index"_s, u"-1"_s ).toInt();
221 if ( index >= 0 )
222 {
223 if ( fillSymbolElement.hasChildNodes() )
224 {
225 const QDomElement symbolElement = fillSymbolElement.firstChildElement( u"symbol"_s );
226 setFillSymbolAt( index, QgsSymbolLayerUtils::loadSymbol< QgsFillSymbol >( symbolElement, context ).release() );
227 }
228 else
229 {
230 setFillSymbolAt( index, nullptr );
231 }
232 }
233 }
234
235 return true;
236}
237
242
244{
245 QgsBarChartPlot *chart = dynamic_cast<QgsBarChartPlot *>( plot );
246 if ( !chart )
247 {
248 return nullptr;
249 }
250
251 return new QgsVectorLayerXyPlotDataGatherer( chart->xAxis().type() );
252}
253
255{
256 if ( !plot )
257 {
258 return;
259 }
260
261 if ( const Qgs2DXyPlot *plotXy = dynamic_cast<const Qgs2DXyPlot *>( plot ) )
262 {
264 }
265 else if ( const Qgs2DPlot *plotPie = dynamic_cast<const Qgs2DPlot *>( plot ) )
266 {
268 }
269}
@ Categorical
The axis represents categories.
Definition qgis.h:3450
@ Interval
The axis represents a range of values.
Definition qgis.h:3449
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.
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.