Hacking Highcharts
Not dogma, just what I'm learning and thinking about right now.
Comments and feedback are welcome on Mastodon.
"If you're thinking without writing, then you just think you're thinking."
—Leslie Lamport
A while ago I saw this amazing article in Wired. The basic idea was to start with a week’s worth of NYC 311 call data, collect the data based on the category of the 311 complaint type, and then show what complaints were made by the hour of the day in which the calls were placed. I found the results (above) to be visually stunning. Don’t you?
I had used the awesome Highcharts library before, so I decided to see if I could create the same effect of having my data “float” above the x-axis, just as in the image above. I quickly found that while very flexible, Highcharts did not offer the chart style I wanted out of the box. Hmm. Cool! We get to hack Highcharts! :) This is how I got it done. (EDIT: Since I completed this project, Highcharts has added this type of chart to their portfolio–TIL it’s called a “steamgraph.”)
Getting Started
This example is going to start with the standard Highcharts demo of a “stacked area” chart, which can be found here. This tutorial assumes you have some basic familiarity with Highcharts, and are comfortable with the code required to produce this “stacked area” demo. If you want to follow along, click the link above, then click the button marked “Edit in Jsfiddle.”
Before we start our hacking, we need to make some cosmetic changes to the demo (these settings can be found under plotOptions
in the jsfiddle Javascript box). First, we get rid of the distracting markers (marker.enabled: false
) and eliminate the border lines on the data (delete lineWidth
and lineColor
), leaving just the color fill. Next, we change the chart.type
from area
to areaspline.
This is necessary for the curved line effect we are going for. Finally, we get rid of the labels on the y-axis (delete other yAxis
settings and set visible: false
), since they will no longer be meaningful for our visualization.
So our starting point should now look like this (view on jsfiddle):
Tricking Highcharts
Our goal is to raise the colored areas on the chart to make them centered on the y-axis. Each point on the x-axis has to be raised by a different amount. How can we do that? My solution was to use a variation on the standard algorithm for centering text in a fixed-width column: that is, to pad each line of text with a number of leading spaces equal to (column.width - text.length) / 2.
Translating that approach to our problem, we are going to add a new data set to the chart. I call this set floatingOffset
(hey, I did it in JavaScript). Like the other data sets in the example, floatingOffset
will consist of seven data points, but these data points will be calculated specifically to “lift” or “float” the colored areas of the chart so they are centered on the y-axis.
The first thing we need to calculate is our “column width” or, in our case, the highest point of the data drawn on the chart. For this example, we’ll do the calculations by hand to illustrate the process.
The data sets from jsfiddle at the time of this writing are as follows. Use the data you see in jsfiddle for your own exercise.
[502, 635, 809, 947, 1402, 3634, 5268]
[106, 107, 111, 133, 221, 767, 1766]
[163, 203, 276, 408, 547, 729, 628]
[18, 31, 54, 156, 339, 818, 1201]
[2, 2, 2, 6, 13, 30, 46]
We can see from the chart above that the highest point is the final data point in each group, so we can calculate our max height from the last item in each array as:
5268 + 1766 + 628 + 1201 + 46 = 8909
We’re going to round this working number up to 10,000 for two reasons. One, to make our math easier. ;) And two, to provide a little extra padding, which is needed to keep our chart from actually touching the x-axis at its widest point (the result if we use the exact height calculated).
Next, we need to calculate the height of the plotted data at each data point. Just as we did when calculating the highest point, we are simply going to take the sum total of our data arrays at each point. In math-y lingo, we’re going to transpose our collection of arrays, and then sum each of the transposed arrays. Using the data arrays listed above (remember, your numbers may be different), we get the following:
[791, 978, 1252, 1650, 2522, 5978, 8909] (full disclosure: I used a spreadsheet)
Creating floatingOffset
Now that we (finally!) have the necessary data, we can calculate the floatingOffset
series by subtracting each of the above numbers from 10,000:
[9209, 9022, 8748, 8350, 7478, 4022, 1091] (still using my spreadsheet . . .)
and then dividing the results each by 2 (and rounding off):
[4605, 4511, 4374, 4175, 3739, 2011, 546]
This is our floatingOffset
data set. Let’s add it to the chart and see what we get (view in jsfiddle):
Okay, that’s a good start, but there are issues. We don’t really want to see “floatingOffset” in the legend, and we definitely don’t want to see the floatingOffset
area colored pink on the chart.
Final touches
The first issue is solved easily enough by adding a new attribute to our floatingOffset
data set: showInLegend: false
.
With that handled, I came up with two ways to keep floatingOffset
from actually appearing on the chart. The first was to simply set the fillColor
attribute for the series to a fully transparent color that would make it invisible on the chart (e.g., rgba(0,0,0,0)
).
The above method felt kind of hacky to me, so I ended up implementing something that felt more idiomatic. First, I set the visible
attribute of the floatingOffset
series to false
. However, because the default behavior of Highcharts is to collapse any hidden data series, I also had to set the chart’s ignoreHiddenSeries
attribute to false
.
These two changes give us the final effect we desire (view in jsfiddle):
And that’s it! Thanks for reading! :)
You can see the live project that generated my opening illustration here, and the source code for the project on GitHub.