SVGGraph Gantt Charts
Skip to:
Page 2:
A Gantt chart is a type of horizontal floating bar graph, used for showing
the schedule and progress of a project. Each bar represents a task, and these
tasks can be grouped together to show sub-projects. The chart can also show
milestones - markers that show imprtant points in the project. The graph type
name that you should use to make SVGGraph render these is "GanttChart
".
Gantt charts are quite complicated, so to organise this a bit better I have spread the information across two pages. The most common options are explained on this page, with advanced topics and the lists of options and structured data fields on the second page.
Full example
Before I explain how to use GanttChart
, here is an example showing a main
project broken down into two sub-projects, with four tasks and a milestone.
The task bars are standard floating bar chart bars, though they do show the percentage completion as a gauge in the filled area. Group bars are displayed with downward points at each end, and these too show the percentage completion inside. Milestones are displayed as diamond-shaped markers, and the lines with arrow heads display dependencies between tasks.
There are plenty of options for configuring Gantt charts, though the task bars, milestones and group bars are all specified using structured data.
Automatic height
One important change for Gantt charts is the added support for an automatic
SVG document height. Supplying the string "auto"
as the graph
height will make SVGGraph calculate a height for the graph based on the number
of entries, font sizes, bar widths and other options.
$graph = new Goat1000\SVGGraph\SVGGraph(670, 'auto', $options);
You can continue to specify your own height for the graph and the bars and milestone markers will be spread out or squashed together to fit the available space.
Task bars
The simplest Gantt chart contains only task bars. For this example I have
set the bar_round
option to 10, rounding off the ends of the bars.
Since Gantt charts require more than simple coordinate pairs, you must use structured data for passing the bar values to SVGGraph:
$options = [
'structure' => [
'key' => 0, 'value' => 1, 'end' => 2,
],
'bar_round' => 10,
];
$values = [
['A simple task', '2022-04-01', '2022-04-13'],
['Second task', '2022-04-05', '2022-04-20'],
['Third task', '2022-05-02', '2022-05-17'],
['Final task', '2022-05-16', '2022-05-20'],
];
The example is using the task names as the key field, with the value field
and end
field containing the start and end dates of the tasks.
Repeated item names
If you want to repeat the name of a task, group or milestone, you must use a
separate unique key field and the axis_text
field:
In this example “Testing” appears twice, so I have had to use a key field:
$options = [
'structure' => [
'key' => 0, 'axis_text' => 1, 'value' => 2, 'end' => 3,
],
'bar_round' => 10,
];
$values = [
['A0', 'A simple task', '2022-04-01', '2022-04-13'],
['A2a', 'Testing', '2022-04-13', '2022-04-15'],
['A1', 'Second task', '2022-04-05', '2022-04-20'],
['A2', 'Testing', '2022-04-20', '2022-04-21'],
//['A3', 'Third task', '2022-05-02', '2022-05-17'],
['A4', 'Final task', '2022-05-16', '2022-05-20'],
['A5', 'Testing', '2022-05-20', '2022-05-20'],
];
I have used alphanumeric string keys for this example because it allows me to comment out one of the values. If I had used numeric keys and commented one out there would have been a gap in the chart where the missing item should be. Apart from that, numeric keys should work fine - just make sure that the key values are sequential integers starting at 0.
Completion percentages
To show how far the project has progressed you can give each task a completion percentage.
To do this I added a complete
field to the data structure
and set the colour used for the complete part. SVGGraph uses the dataset
0 colour for incomplete bars and the dataset 1 colour for the complete
gauge inside the bar:
$options = [
'structure' => [
'key' => 0, 'axis_text' => 1, 'value' => 2, 'end' => 3,
'complete' => 'c',
],
'bar_round' => 10,
'show_data_labels' => true,
'pad_right' => 20,
];
$values = [
['A0', 'A simple task', '2022-04-01', '2022-04-13', 'c' => 50],
['A2a', 'Testing', '2022-04-13', '2022-04-15'],
['A1', 'Second task', '2022-04-05', '2022-04-20', 'c' => 20],
['A2', 'Testing', '2022-04-20', '2022-04-21'],
['A3', 'Third task', '2022-05-02', '2022-05-17', 'c' => 60],
['A4', 'Final task', '2022-05-16', '2022-05-20', 'c' => 78],
['A5', 'Testing', '2022-05-20', '2022-05-20'],
];
// orange-red-purple range for bars, green-blue for complete
$graph->colourRangeHexHSL(0, '#c50', '#c59');
$graph->colourRangeHexHSL(1, '#6a0', '#6a9');
The complete
field is set to "c"
to use the
relevant parts of the data array. I've also enabled data labels, which
display the completion percentage. The extra pad_right
is
there to provide a bit more space for the labels.
Milestones
A Gantt chart with nothing but milestones seems a bit odd, but SVGGraph does support doing that. Here is what it can look like:
The milestone
field is set in the structure to use the
"m"
values from the data. These are simple booleans to tell
SVGGraph that the row of data represents a milestone. Milestones are
points in time, so the end
value is not used or required.
$options = [
'structure' => [
'key' => 0, 'axis_text' => 1, 'value' => 2, 'end' => 3,
'milestone' => 'm',
],
'show_data_labels' => true,
];
$values = [
['M1', 'Start here', '2022-05-02', 'm' => true],
['M2', 'Milestone 2', '2022-05-09', 'm' => true],
['M3', 'Milestone 3', '2022-05-16', 'm' => true],
['M4', 'Another', '2022-05-17', 'm' => true],
['M5', 'Another', '2022-05-18', 'm' => true],
['M6', 'Last one', '2022-06-02', 'm' => true],
];
The show_data_labels
option is used again to label the milestones
on the chart. There are options for setting the colour, size and marker type used
for the milestones, but I haven't used any here.
Multiple task bars/milestones
It is possible to show multiple task bars or milestones on a single row of the Gantt chart, or even a mixture of the two. You can do this by using multiple datasets for your task bars and milestones.
The structure for this example is a bit more complicated than most, so I have lined up the numbered fields in the data array to make it a bit clearer:
$options = [
'auto_fit' => true,
'structure' => [
'key' => 0, 'axis_text' => 1,
'value' => [2, 4, 6, 8, 10],
'end' => [3, 5, 7, 9, 11],
'complete' => ['c1', 'c2', 'c3', 'c4', 'c5'],
'milestone' => ['m1', 'm2', 'm3', 'm4', 'm5'],
'label' => ['l1', 'l2', 'l3', 'l4', 'l5'],
],
];
$values = [
// 0 1 2 3 4 5 6
['A0', 'A simple task', '2022-04-01', '2022-04-13', '2022-05-01', '2022-05-15', '2022-04-20',
'c1' => 50, 'c2' => 25, 'm3' => true],
['A2a', 'Testing', '2022-04-13', '2022-04-15', '2022-05-15', '2022-05-17', '2022-05-21',
'm3' => true, 'l3' => 'OK'],
['A1', 'Second task', '2022-04-05', '2022-04-20',
'c1' => 20],
['A2', 'Testing', '2022-04-20', '2022-04-21'],
['A3', 'Third task', '2022-05-02', '2022-05-17',
'c1' => 60],
['A4', 'Final task', '2022-05-16', '2022-05-20',
'c1' => 78],
['A5', 'Testing', '2022-05-20', '2022-05-20', '2022-04-05', null, '2022-04-19',
'm2' => true, 'm3' => true, 'l2' => 'Test 1', 'l3' => 'Test 2'],
];
The data fields 0 and 1 are the key field and axis text, as in the previous example.
Instead of a single value
and end
field there are now
up to five of each. Because there are multiple bars there must also be multiple
complete
fields, one for each bar. I have also added multiple
label
fields to allow each bar or milestone to have its own
label.
The milestones are enabled with the five fields m1
to m5
.
I've chosen the names of these fields so that m3
being true
means that the third value
field is a milestone, etc. If you prefer,
you could name all the fields, or even not name any and just rely on the numeric
index.
Bar/milestone order and overlapping
Note that the two milestones on the bottom row of the graph have dates before the task bar that comes before them in the data. SVGGraph does not do anything to prevent bars and milestones from overlapping, so you could position milestones on top of bars, or vice-versa if that is what you want. The bars and milestones are drawn in order of dataset, not in date order.
Group bars
Group bars are used to group multiple task bars and milestones together. They usually have downward-pointing corners at either end.
This example sets the group
field in the structure to point at
the "g"
fields in the data array. There is only one group defined,
with a value of 4
, and that means the four entries following the group
are included in the group.
$options = [
'structure' => [
'key' => 0, 'axis_text' => 1, 'value' => 2, 'end' => 3,
'complete' => 'c',
'group' => 'g',
],
'bar_round' => 10,
'show_data_labels' => true,
'pad_right' => 20,
];
$values = [
['G0', 'A Group bar', 'g' => 4],
['A0', 'A simple task', '2022-04-01', '2022-04-13', 'c' => 50],
['A2a', 'Testing', '2022-04-13', '2022-04-15'],
['A1', 'Second task', '2022-04-05', '2022-04-20', 'c' => 20],
['A2', 'Testing', '2022-04-20', '2022-04-21'],
['A3', 'Third task', '2022-05-02', '2022-05-17', 'c' => 60],
['A4', 'Final task', '2022-05-16', '2022-05-20', 'c' => 78],
['A5', 'Testing', '2022-05-20', '2022-05-20'],
];
If you want to include all the following entries in the group, you could use
a larger number, or instead of a number use the string "*"
. In fact,
any non-numeric string would work, since strings are used for nested group levels.
The completion gauge inside the group bar is automatically calculated from all the bars inside the group, so you don't need to do anything to make it work.
Nested groups
To put groups inside other groups is quite easy - just pick a name for each
level and use it as the value of the group
field.
This example has one top-level group and two sub-groups. The field structure is the same in this example, but the data values have changed:
$values = [
['G0', 'Top-level group', 'g' => 'top'],
['G1', 'Sub-group 1', 'g' => 'level 1'],
['A0', 'A simple task', '2022-04-01', '2022-04-13', 'c' => 50],
['A2a', 'Testing', '2022-04-13', '2022-04-15'],
['A1', 'Second task', '2022-04-05', '2022-04-20', 'c' => 20],
['A2', 'Testing', '2022-04-20', '2022-04-21'],
['G2', 'Sub-group 2', 'g' => 'level 1'],
['A3', 'Third task', '2022-05-02', '2022-05-17', 'c' => 60],
['A4', 'Final task', '2022-05-16', '2022-05-20', 'c' => 78],
['A5', 'Testing', '2022-05-20', '2022-05-20'],
];
The “Top-level group” group has a group
value of "top"
,
which is just an arbitrary name for that level. The two sub-groups have a group
value of "level 1"
, another arbitrary name.
SVGGraph puts every task bar or milestone below a named group into that group. When it finds a new group name it starts a new sub-group. A group ends when the data contains the same group name again, starting a new group at the same level.
Writing that out makes it sound really complicated - here's another example, this time with a top level, sub-level and sub-sub-level:
And here is the data for it, with the rows indented to match the chart:
$values = [
['G0', 'Top-level group', 'g' => 'top'],
['G1', 'Sub-group 1', 'g' => 'level 1'],
['A0', 'A simple task', '2022-04-01', '2022-04-13', 'c' => 50],
['A2a', 'Testing', '2022-04-13', '2022-04-15'],
['G1a', 'Sub-sub-group', 'g' => 'l 2'],
['A1', 'Second task', '2022-04-05', '2022-04-20', 'c' => 20],
['A2', 'Testing', '2022-04-20', '2022-04-21'],
['G2', 'Sub-group 2', 'g' => 'level 1'],
['A3', 'Third task', '2022-05-02', '2022-05-17', 'c' => 60],
['G2a', 'Sub-sub-group', 'g' => 'l 2'],
['A4', 'Final task', '2022-05-16', '2022-05-20', 'c' => 78],
['A5', 'Testing', '2022-05-20', '2022-05-20'],
];
There is one top-level group called "top"
, containing
everything else because the group name "top"
doesn't come up
again.
“Sub-group 1” is called "level 1"
and contains everything
until the name "level 1"
is used again for “Sub-group 2”.
The name "l 2"
is used for the sub-sub-group inside "level 1"
.
It is not repeated inside the parent group, so it ends when its parent group ends.