Sign in Join
Wiki copytemporary

TAChart Tutorial: Stacked BarSeries: what changed

Revision 1 to 2. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11## Introduction
22
33![](/wiki-images/7/7e/StackedBarSeries_Final.png)
44
55In the [[TAChart Tutorial: BarSeries|BarSeries tutorial]] we had demonstrated how to create a chart with bar series arranged in a side-by-side manner. Stacked bars are another popular variant of bar charts where the grouped data are stacked onto each other. In this way, the individual contributions and their sum can be shown within the same plot. There is also a variant in which the total bar heights are normalized to 100% to emphasize the relative contributions [1](http://blog.visual.ly/how-groups-stack-up-when-to-use-grouped-vs-stacked-column-charts/).
66
77We'll apply this technique in the current tutorial to plot the progress in generation of renewable energies in Germany between 1990 and 2012; the data are taken from [wikipedia](http://en.wikipedia.org/wiki/Renewable_energy_in_Germany) and cover hydropower, wind energy, energy from bio mass, photovoltaics, and geothermal energy.
88
99Besides [[TAChart documentation|TBarSeries]] you'll meet [[TAChart documentation|TRandomChartSource]], [[TAChart documentation|TCalculatedChartSource]], and `TChartStyles` in this tutorial unit.
1010
1111As usual, you must have some basic knowledge of Lazarus, Object Pascal, and the key concepts of TAChart to follow the tutorial. If you have never worked with bar charts it is a good idea to look at the related tutorial explaining the [[TAChart Tutorial: BarSeries|side-by-side arrangement of bars]]. For additional reading we recommend the [[TAChart documentation]].
1212
1313## Designing the chart with random data
1414
1515### Preparations
1616
1717Create a new project. Add a `TAChart` component, and align it to `alClient` to completely fill the form. Maybe you should increase the size of the form a bit, but you can do this later when you see the data.
1818
1919Before using the "real data" we'll arrange and format the chart to our liking. For this purpose it is advantageous to get a quick response to the changes made in the object inspector. Therefore, we propose to use a [[TAChart documentation|TRandomChartSource]] linked to the BarSeries. A RandomChartSource just produces random data, but is a great helper to to optimize the appearence of the chart without having to recompile the project again and again. Of course, at the end, we will remove the RandomChartSource and replace it with the data of interest.
2020
2121So, let's start: Add a `TBarSeries` and a `TRandomChartSource` to the form, and link the RandomChartSource to the `Source` property of the BarSeries. (If you need more details have a look at the [[TAChart Tutorial: BarSeries|other BarSeries tutorial]]). Select the RandomChartSource, and set `XMin` to 1990 and `XMax` to 2012 (which is the range of our original data in the [wikipedia article](http://en.wikipedia.org/wiki/Renewable_energy_in_Germany)). In total, these are 23 years, so you set `PointsNumber` to 23 in order to get a bar for each year. Finally, set `YMax` to `1` (or any other numbers that you prefer). Now a bar series pops into the chart:
2222
2323![](/wiki-images/e/e4/StackedBarSeries_Step1.png)
2424
2525### Multiple y values
2626
2727If you had read the other [[TAChart Tutorial: BarSeries|BarSeries tutorial]] you may be surprised now: In that tutorial, we had used a single series for each group of bars. In our final renewable energy plot there will be several types of bars, one for each energy kind. So, shouldn't we add some more series?
2828
2929No. We only need one since we cannot follow the strategy of the side-by-side series here. This is because `TAChart` cannot stack the bars of different series onto each other. Each bar is drawn from a common "zero level" up (or down) to the data value - no way to shift the next bar up to the end of the previous bar.
3030
3131What to do?
3232
3333For stacking of series, `TAChart` offers the special concept of multiple y values per series data point. If you look into unit `TACustomSource` you'll find the record
3434
3535``` pascal
3636type
3737 TChartDataItem = packed record
3838 public
3939 X, Y: Double;
4040 Color: TChartColor;
4141 Text: String;
4242 YList: TDoubleDynArray;
4343 ...
4444 end;
4545```
4646
4747This is the internal structure of the data plotted in a series. Besides the usual `x` and `y` coordinates that you know from other plot types and tutorials, there is also a data field `YList` which reserves storage for additional y values. The chart source provides the property `YCount` to define the length of the array `YList` - select the RandomChartSource and have a look at the Object Inspector. But you should know that `YCount` includes also the standard y value, the length of the `YList` array for the other y values, therefore, is `YCount-1`.
4848
4949Our final plot will contain five column types ("hydro power", "wind energy", "bio mass", "photo-voltaics", and "geothermal"). Therefore, enter `5` for `YCount`. This creates stacks of five bars.
5050
5151![](/wiki-images/4/46/StackedBarSeries_Step2.png)
5252
5353### Using ChartStyles
5454
5555Unfortunately, all bars are in the same color. How can we modify the color of each bar level?
5656
5757We need another component to address this: **ChartStyles**. This is a collection of `TChartStyle` components which allow to modify the appearance ("style") of each set of bars.
5858
5959Go to the component palette, find the 9th icon (the one with the brush in front of the colored bars), and add the component to the form.
6060
6161![](/wiki-images/6/6c/ChartStyles.png)
6262
6363At first you have to link the BarSeries to the ChartStyles component. The series has a corresponding property `Styles` for this purpose.
6464
6565Then, select the component, and go to its `Styles` property in the object inspector. Click on the ellipsis button "..." to open the ChartStyles editor. Here you can add a style for each group of bars by clicking on the "+" button five times. Alternatively you can right-click on the "Styles" node in the object tree of `ChartStyles1` and select "Add item".
6666
6767![](/wiki-images/c/c5/ChartStylesEditor.png)
6868
6969![](/wiki-images/a/a8/ChartStylesEditor_Properties.png)
7070
7171In the the ChartStyle editor you can select each style and you'll see the style's properties in the object inspector. The `TChartStyle` item with the index 0 belongs to the bottom-most bar, index 1 belongs to the next bar, etc. For changing the color of the bars, modify the `Brush.Color` of the style. The chart immediately updates and displays the corresponding bar in the new color. Repeat this with the other styles. Have a look at the other properties of the `TChartStyle` to learn what else can be adapted.
7272
7373### Showing ChartStyles in the legend
7474
7575Using the `ChartStyle` property `Text` you can assign a legend item for each bar level. Set `Text` to "Hydropower", "Wind energy", "Bio mass", "Photovoltaic", and "Geothermal" for the styles, starting with index 0.
7676
7777In order to show the legend you know that you have to set the chart's `Legend.Visible` property to `true`; it is `false` by default. The legend, however, displays only a single entry, a red box corresponding to the bar series without usage of `ChartStyles`. In order to correct that, select the BarSeries, and open the node `Legend` in the object inspector. The property `Legend.Multiplicity` is still at `lmSingle` which produces only a single legend item per series. Change it to `lmStyle` instead. This is the correct setting when the series uses styles. Now the chart reads the `Text` values of each style and adds them to the legend.
7878
7979![](/wiki-images/9/97/StackedBarSeries_Step3.png)
8080
8181### Normalizing bar stacks to 100%
8282
8383In order to emphasize the evolution of the relative contributions of the individual bar groups stacked bar charts are often normalized to 100%, i.e. each bar value is replaced by its percentage of the total stack heights.
8484
8585`TAChart` can do this with a little modification of the current program. The key ingredient is usage of a `TCalculatedChartSource` which picks up the data from the currently used chart source and calculates the relative contributions.
8686
8787Add a `TCalculatedChartSource` to the form. Set its property `Origin` to point to our RandomChartSource. This means that the CalculatedChartSource will perform its calculations based on the data of our chart. It is important that you leave `AccumulationMethod` at its default, `camNone`, which means that the input data are passed to the output unchanged. The other options would combine input data values to new values: `camAverage`, for example, would cause calculation of the average of the data points within the `AccumulationRange`. But how do we get the percentage calculation if `camNone` does not change anything? Simple - there is a `Percentage` property! The normalization is applied after the accumulation calculations. Set `Percentage` to `true`.
8888
8989We don't see a change in the chart yet, because the series still gets its data from the RandomChartSource. Go to the BarSeries and select the CalculatedChartSource as `Source`.
9090
9191Here we are!
9292
9393![](/wiki-images/8/8b/StackedBarSeries_Percentage.png)
9494
9595## Plotting real data
9696
9797Now that the chart is practically finished it's getting time to remove the random data and replace them with the "real data". As noted in the introduction, we want to plot the evolution of renewable energy generation during various years. The data can be found at the end of the [wikipedia article](http://en.wikipedia.org/wiki/Renewable_energy_in_Germany).
9898
9999At first, do the bravery test: delete the RandomChartSource, we don't need it any more - all the previous data are gone, and the chart becomes empty...
100100
101101How do we get the new data to the series?
102102
103103### Providing real data to the bar series
104104
105105Add a `TListChartSource` to the form. It will house the data. Please remember that every data point consists of five y values in our project. Therefore, you must set the `YCount` property of this chart source to `5` again. To put the multiple y data into the list source it has a method `AddXYList` where you can specify all y values at once.
106106
107107``` pascal
108108function TListChartSource.AddXYList(
109109 AX: Double; const AY: array of Double;
110110 const ALabel: String = '';
111111 AColor: TChartColor = clTAColor): Integer;
112112```
113113
114114But don't forget that the count of `Double` array elements must match the `YCount` specified.
115115
116116For simplicity we type the data from the wikipedia page and call `AddXYList` in the `OnCreate` event of the form:
117117
118118``` pascal
119119procedure TForm1.FormCreate(Sender: TObject);
120120begin
121121 with ListChartSource1 do begin
122122 // Y0 = Hydropower
123123 // Y1 = Wind energy (off-shore + on-shore)
124124 // Y2 = Biomass + biogenic share of waste
125125 // Y3 = Photovoltaics
126126 // Y4 = Geothermal energy
127127 // all values in GWh
128128 // source: http://en.wikipedia.org/wiki/Renewable_energy_in_Germany
129129 AddXYList(1990, [15580, 71, 221+1213, 0.6, 0]);
130130 AddXYList(1991, [15402, 100, 260+1211, 1.6, 0]);
131131 AddXYList(1992, [18091, 275, 296+1262, 3.2, 0]);
132132 AddXYList(1993, [18526, 600, 433+1203, 5.8, 0]);
133133 AddXYList(1994, [19501, 909, 569+1306, 8, 0]);
134134 AddXYList(1995, [20747, 1500, 665+1348, 11, 0]);
135135 AddXYList(1996, [18340, 2032, 759+1343, 16, 0]);
136136 AddXYList(1997, [18453, 2955, 880+1397, 26, 0]);
137137 AddXYList(1998, [18452, 4489, 1642+1618, 32, 0]);
138138 AddXYList(1999, [20686, 5528, 1849+1740, 42, 0]);
139139 AddXYList(2000, [24867, 9513, 2893+1844, 64, 0]);
140140 AddXYList(2001, [23241, 10509, 3348+1859, 75, 0]);
141141 AddXYList(2002, [23662, 15786, 4089+1949, 162, 0]);
142142 AddXYList(2003, [17722, 18713, 6086+2161, 313, 0]);
143143 AddXYList(2004, [19910, 25509, 7960+2117, 556, 0.2]);
144144 AddXYList(2005, [19576, 27229, 10978+3047, 1282, 0.2]);
145145 AddXYList(2006, [20042, 30710, 14841+3844, 2220, 0.4]);
146146 AddXYList(2007, [21169, 39713, 19760+4521, 3075, 0.4]);
147147 AddXYList(2008, [20446, 40574, 22872+4659, 4420, 17.6]);
148148 AddXYList(2009, [19036, 30602+38, 25989+4352, 6583, 18.8]);
149149 AddXYList(2010, [20956, 37619+174, 29085+4781, 11683, 27.7]);
150150 AddXYList(2011, [17674, 48315+568, 31920+5000, 19340, 18.8]);
151151 AddXYList(2012, [21200, 45325+675, 35950+4900, 28000, 25.4]);
152152 end;
153153end;
154154```
155155
156156Don't forget to link the ListChartSource it to the `Origin` of the CalculatedChartsource. Then compile. Here is the output when `CalculatedChartSource.Percentage = true`:
157157
158158![](/wiki-images/c/c9/StackedBarSeries_Percentage_RealData.png)
159159
160160### Refinements
161161
162162To complete the chart add a title to the axes ("Year" for the horizontal axis, "Energy, GWh" for the vertical axis. In addition, it would be fine to show a title above the chart ("Generation of renewable energy in Germany"), and a footer with the wikipedia reference.
163163
164164Change to `BackColor` of the chart to `clWhite`. Reduce the `Margins.Bottom` to `0` in order to remove the gap between bars and `x` axis.
165165
166166Finally you could add a panel with a checkbox to toggle between "normal" and "percentage" view of the stacked bar chart. Let's leave the code to be assigned to the `OnClick` event of the checkbox as a home work...
167167
168168![](/wiki-images/7/7e/StackedBarSeries_Final.png)
169169
170170## Summary
171171
172172These are the steps to create a stacked bar chart:
173173
174174- You need only one series, but it must contain multiple y values
175175- In the chartsource, set `YCount` to the number of stack levels required.
176176- Add data to the chartsource by calling `AddXYList`. Be aware that the number of values passed to the array parameter matches the `YCount` specified.
177177- Add a `TChartStyles` component to the form and link it to the propery `Style` of the BarSeries
178178- For each stack level, add a `TChartStyle`.
179179- Select the color of the stacked bar by means of the corresponding ChartStyle. Moreover, enter the legend text in the field `Text` of the ChartStyle.
180180- To show the ChartStyle text entries in the legend use the value `lmStyle` for the `Legend.Multiplicity` of the series.
181181- For creating a normalized stacked bar chart, add a `TCalculatedChartSource` to the form. Link its `Origin` property to the chartsource providing the data for plotting. Use the CalculatedChartSource as `Source` of the series. Set the CalculatedChartSource's `Percentage` to `true` in order to activate normalization to 100%.
182182- The same methodology can be applied to create stacked area and line series.
183183- Not mentioned in the text, but the BarSeries of Lazarus v2.0+ exposes a property `Stacked` which switches between stacked and side-by-side arrangement of the y values. This simplifies the creation of side-by-side bar charts.
184184
185185## Source code
186186
187187### Project file
188188
189189``` pascal
190190program project1;
191191
192192{$mode objfpc}{$H+}
193193
194194uses
195195 {$IFDEF UNIX}{$IFDEF UseCThreads}
196196 cthreads,
197197 {$ENDIF}{$ENDIF}
198198 Interfaces, // this includes the LCL widgetset
199199 Forms, Unit1, tachartlazaruspkg
200200 { you can add units after this };
201201
202202{$R *.res}
203203
204204begin
205205 RequireDerivedFormResource := True;
206206 Application.Initialize;
207207 Application.CreateForm(TForm1, Form1);
208208 Application.Run;
209209end.
210210```
211211
212212### Unit1.pas
213213
214214``` pascal
215215unit Unit1;
216216
217217{$mode objfpc}{$H+}
218218
219219interface
220220
221221uses
222222 Classes, SysUtils, FileUtil, TAGraph, TASeries, TASources, TAStyles,
223223 Forms, Controls, Graphics, Dialogs, ExtCtrls, StdCtrls;
224224
225225type
226226
227227 { TForm1 }
228228
229229 TForm1 = class(TForm)
230230 CalculatedChartSource1: TCalculatedChartSource;
231231 Chart1: TChart;
232232 Chart1BarSeries1: TBarSeries;
233233 ChartStyles1: TChartStyles;
234234 CheckBox1: TCheckBox;
235235 ListChartSource1: TListChartSource;
236236 Panel1: TPanel;
237237 procedure CheckBox1Click(Sender: TObject);
238238 procedure FormCreate(Sender: TObject);
239239 private
240240 { private declarations }
241241 public
242242 { public declarations }
243243 end;
244244
245245var
246246 Form1: TForm1;
247247
248248implementation
249249
250250{$R *.lfm}
251251
252252{ TForm1 }
253253
254254procedure TForm1.FormCreate(Sender: TObject);
255255begin
256256 with ListChartSource1 do begin
257257 // Y0 = Hydropower
258258 // Y1 = Wind energy (off-shore + on-shore)
259259 // Y2 = Biomass + biogenic share of waste
260260 // Y3 = Photovoltaics
261261 // Y4 = Geothermal energy
262262 // all values in GWh
263263 // source: http://en.wikipedia.org/wiki/Renewable_energy_in_Germany
264264 AddXYList(1990, [15580, 71, 221+1213, 0.6, 0]);
265265 AddXYList(1991, [15402, 100, 260+1211, 1.6, 0]);
266266 AddXYList(1992, [18091, 275, 296+1262, 3.2, 0]);
267267 AddXYList(1993, [18526, 600, 433+1203, 5.8, 0]);
268268 AddXYList(1994, [19501, 909, 569+1306, 8, 0]);
269269 AddXYList(1995, [20747, 1500, 665+1348, 11, 0]);
270270 AddXYList(1996, [18340, 2032, 759+1343, 16, 0]);
271271 AddXYList(1997, [18453, 2955, 880+1397, 26, 0]);
272272 AddXYList(1998, [18452, 4489, 1642+1618, 32, 0]);
273273 AddXYList(1999, [20686, 5528, 1849+1740, 42, 0]);
274274 AddXYList(2000, [24867, 9513, 2893+1844, 64, 0]);
275275 AddXYList(2001, [23241, 10509, 3348+1859, 75, 0]);
276276 AddXYList(2002, [23662, 15786, 4089+1949, 162, 0]);
277277 AddXYList(2003, [17722, 18713, 6086+2161, 313, 0]);
278278 AddXYList(2004, [19910, 25509, 7960+2117, 556, 0.2]);
279279 AddXYList(2005, [19576, 27229, 10978+3047, 1282, 0.2]);
280280 AddXYList(2006, [20042, 30710, 14841+3844, 2220, 0.4]);
281281 AddXYList(2007, [21169, 39713, 19760+4521, 3075, 0.4]);
282282 AddXYList(2008, [20446, 40574, 22872+4659, 4420, 17.6]);
283283 AddXYList(2009, [19036, 30602+38, 25989+4352, 6583, 18.8]);
284284 AddXYList(2010, [20956, 37619+174, 29085+4781, 11683, 27.7]);
285285 AddXYList(2011, [17674, 48315+568, 31920+5000, 19340, 18.8]);
286286 AddXYList(2012, [21200, 45325+675, 35950+4900, 28000, 25.4]);
287287 end;
288288end;
289289
290290procedure TForm1.CheckBox1Click(Sender: TObject);
291291begin
292292 CalculatedChartSource1.Percentage := Checkbox1.Checked;
293293 if Checkbox1.Checked then
294294 Chart1.LeftAxis.Title.Caption := 'Contribution, %'
295295 else
296296 Chart1.LeftAxis.Title.Caption := 'Energy, GWh';
297297end;
298298
299299end.
300300```
301301
302302### Unit1.lfm
303303
304-``` pascal
304+``` lfm
305305object Form1: TForm1
306306 Left = 596
307307 Height = 270
308308 Top = 353
309309 Width = 517
310310 Caption = 'Form1'
311311 ClientHeight = 270
312312 ClientWidth = 517
313313 OnCreate = FormCreate
314314 LCLVersion = '1.3'
315315 object Chart1: TChart
316316 Left = 0
317317 Height = 248
318318 Top = 22
319319 Width = 517
320320 AxisList = <
321321 item
322322 Minors = <>
323323 Title.LabelFont.Orientation = 900
324324 Title.LabelFont.Style = [fsBold]
325325 Title.Visible = True
326326 Title.Caption = 'Energy, GWh'
327327 end
328328 item
329329 Alignment = calBottom
330330 Minors = <>
331331 Title.LabelFont.Style = [fsBold]
332332 Title.Visible = True
333333 Title.Caption = 'Year'
334334 end>
335335 BackColor = clWhite
336336 Foot.Alignment = taLeftJustify
337337 Foot.Brush.Color = clBtnFace
338338 Foot.Font.Color = clGray
339339 Foot.Text.Strings = (
340340 'Source: http://en.wikipedia.org/wiki/Renewable_energy_in_Germany'
341341 )
342342 Foot.Visible = True
343343 Legend.Visible = True
344344 Margins.Bottom = 0
345345 Title.Brush.Color = clBtnFace
346346 Title.Font.Color = clBlue
347347 Title.Font.Style = [fsBold]
348348 Title.Text.Strings = (
349349 'Generation of renewable energy in Germany'
350350 )
351351 Title.Visible = True
352352 Align = alClient
353353 ParentColor = False
354354 object Chart1BarSeries1: TBarSeries
355355 Legend.Multiplicity = lmStyle
356356 BarBrush.Color = clRed
357357 Source = CalculatedChartSource1
358358 Styles = ChartStyles1
359359 end
360360 end
361361 object Panel1: TPanel
362362 Left = 0
363363 Height = 22
364364 Top = 0
365365 Width = 517
366366 Align = alTop
367367 BevelOuter = bvNone
368368 ClientHeight = 22
369369 ClientWidth = 517
370370 TabOrder = 1
371371 object CheckBox1: TCheckBox
372372 Left = 8
373373 Height = 19
374374 Top = 0
375375 Width = 119
376376 Caption = 'Normalize to 100%'
377377 OnClick = CheckBox1Click
378378 TabOrder = 0
379379 end
380380 end
381381 object ChartStyles1: TChartStyles
382382 Styles = <
383383 item
384384 Brush.Color = clBlue
385385 Text = 'Hydropower'
386386 end
387387 item
388388 Brush.Color = clSkyBlue
389389 Text = 'Wind energy'
390390 end
391391 item
392392 Brush.Color = clGreen
393393 Text = 'Biomass'
394394 end
395395 item
396396 Brush.Color = clYellow
397397 Text = 'Photovoltaic'
398398 end
399399 item
400400 Brush.Color = clMaroon
401401 Text = 'Geothermal'
402402 end>
403403 left = 336
404404 top = 96
405405 end
406406 object CalculatedChartSource1: TCalculatedChartSource
407407 Origin = ListChartSource1
408408 left = 104
409409 top = 96
410410 end
411411 object ListChartSource1: TListChartSource
412412 YCount = 5
413413 left = 232
414414 top = 96
415415 end
416416end
417417```
418418
419419---
420420*From the Free Pascal wiki, page "TAChart Tutorial: Stacked BarSeries", as it stood on 2020-02-29 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history