Bar and Pie Charts with RaphaelJS

At my last place we got some cool Google Analytics-style charts going using the excellent RaphaelJS library. This week I’ve been playing with the simpler bar and pie charts, and thought I’d share some findings. RaphaelJS is great, but the examples and documentation are still — like so many open source projects — slim. In particular, I found that the example provided for an interactive pie chart is broken, so I had to play around to get that working.

I did the bar chart first, and found after some searching in the Google Group archive that the labels aren’t quite working in the main branch, but thankfully someone else has a forked version of the bar chart file. If you want to get the labels working properly, go here: http://github.com/iamwilhelm/g.raphael and grab g.bar.js.

I set up a simple report page that displays the same data using either a bar chart or a pie chart depending on the parameter “chart”. The data is simply a count of totals for each month of the year, so there are up to 12 bars/segments shown in each chart. The controller gathers up the data from a query and also prepares the labels for the charts:

  def data_by_month
    @chart = params[:chart]
    months = [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ]

    monthly_totals = MyModel.get_monthly_totals()
    @monthly = []
    @month_names = []
    monthly_totals.each_with_index do |month, i|
      @monthly << month['count'].to_i
      month_name = months[i]
      if (@chart == 'pie')  # Need to add counts to the labels for pie charts
        month_name << " (#{month['count']})"
      end
      @month_names << month_name
    end
  end

One of the awkward things you’ll see above is that the code adds the numbers to the labels if it’s going to show a pie chart. That’s because unfortunately I haven’t yet found a way to show the numbers within the pie chart segments. So instead, I display the legend/labels to the side of the pie chart, with the numbers as part of the labels. For example, instead of just showing “January” it shows “January (35)”. Not great, naturally. The bar charts automatically show the numbers when you hover over the bars, so I didn’t need to do that in the bar chart case.

It’s worth quickly pointing out that when the code’s adding the values to the @monthly array, I had to do a .to_i on it. Otherwise the values went in as strings. Oddly enough, that worked fine for the bar chart, but the pie chart didn’t show any results.

The page itself is as follows:

<%= javascript_include_tag "raphael-min.js" -%>
<%= javascript_include_tag "g.raphael-min.js" -%>
<!-- Using g.bar.js from http://github.com/iamwilhelm/g.raphael with horz label fix -->
<%= javascript_include_tag "g.bar.js" -%>
<%= javascript_include_tag "g.pie-min.js" -%>

<script type="text/javascript">
  //<![CDATA[
  window.onload = function () {
    var r = Raphael("holder", 800, 500),
        fin = function () {
            this.flag = r.g.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
        },
        fout = function () {
            this.flag.animate({opacity: 0}, 300, function () {this.remove();});
        };
    r.g.txtattr.font = "18px 'Fontin Sans', Fontin-Sans, sans-serif";

    r.g.text(160, 10, "Totals by Month for <%= @year -%>");

    <% if (@chart == 'bar') %>
    r.g.barchart(30, 30, 500, 420, [<%= @monthly.to_json -%>]).hover(fin, fout).label([<%= @month_names.to_json -%>]);
    <% else %>
    var pie = r.g.piechart(280, 260, 200, <%= @monthly.to_json -%>, {legend: <%= @month_names.to_json -%>,
        legendpos: "east"});
    pie.hover(function () {
                    this.sector.stop();
                    this.sector.scale(1.1, 1.1, this.cx, this.cy);
                    if (this.label) {
                        this.label[0].stop();
                        this.label[0].scale(1.5);
                        this.label[1].attr({"font-weight": 800});
                    }
                }, function () {
                    this.sector.animate({scale: [1, 1, this.cx, this.cy]}, 500, "bounce");
                    if (this.label) {
                        this.label[0].animate({scale: 1}, 500, "bounce");
                        this.label[1].attr({"font-weight": 400});
                    }
                });
    <% end %>
  };
  //]]>
</script>

<br/><br/>

<h1>Reports : Totals by Month</h1>
<%= link_to('Bar Chart', totals_by_month_path(:chart => 'bar')) %> |
<%= link_to('Pie Chart', totals_by_month_path(:chart => 'pie')) %>

<br/><br/>

<div id="holder"></div>

So, what’s up here? A lot of this code was taken from the RaphaelJS examples, and then munged a bit. After including the files, the onload() function sets up the Raphael object, which is called “holder” to match the id of the <div> down below, which is where the charts will be placed. The functions fin() and fout() are used by the bar chart (so yeah, they should be put inside an if block but I haven’t done that yet). They display/remove the value and do the animation during hover events. The text() call is just to put a title on the chart. In this case it probably makes more sense to leave that in the plain HTML, but it was in the example code so I left it in order to experiment.

If the chart is a bar chart, the line

r.g.barchart(30, 30, 500, 420, [<%= @monthly.to_json -%>]).hover(fin, fout).label([<%= @month_names.to_json -%>]);

is used. Obviously the first bit of code takes the monthly values, which is an array, and drops in the JSON equivalent. Those are the numbers which will be used to create the bar chart. The second bit of code is what places the labels, which in this case are the month names. This threw me off initially, because the function expects an array of arrays, just like the values. This is because it supports “stacked” bar charts — the extra square brackets around the values/labels are important. Without those around the labels, I was getting one character per bar instead of the full string, which was very confusing.

This is what the bar chart looks like (obviously not showing the hover-over which displays the number for each bar):

For the pie chart, the basic call is very similar. The values are passed in the same way, but then the “legend” attribute is used to pass the names of the months (with the totals added in this case). The “legendpos” attribute positions the labels to the north/east/south/west of the chart itself. I started on the west but had trouble with alignment — the chart was getting positioned too close to the labels and would end up partially covering the labels on rollover. Putting the legend on the east worked around that problem for now. The hover() functions were taken verbatim from a pie chart example.

This is what the pie chart looks like (obviously not showing the hover-over, which enlarges the segment and the corresponding legend item):

As you can see, getting these working is very straightforward — the only issues that held me up were due to incomplete documentation or examples not quite working correctly. Hopefully this post can help speed things up for others. RaphaelJS is very powerful library, and the charting plugin that provides the easy charts is a great add-on. Enjoy.

Advertisements
13 comments
  1. Charles said:

    Mason,

    I would like to include your nicely formatted work as a link from my site.

    I’m sure the numbers sharing your findings would increase and I would like to include it as a good “getting started” example for Bar and Pie Charts.

    Regards Charles

  2. Absolutely, feel free to link from your site. You have a lot of good examples and information about RaphaelJS, and if you think this would be useful to people, feel free to link away.

  3. Ico said:

    Very informative article! Can you please post the link to the full source code?

    • Hello, I’m glad that the article is useful. Unfortunately this project is a bit confidential so I can’t provide full source code; I had to edit the code to make it generic before posting it.

      • Ico said:

        Thanks for the quick reply!

        May I ask you what language you are using fo the definition of the def_by_data:

        01 def data_by_month
        02 @chart = params[:chart]
        03 months = [ “Jan”, “Feb”, “Mar”, “Apr”, “May”, “Jun”, “Jul”, “Aug”, “Sep”, “Oct”, “Nov”, “Dec” ]

        16 end

        And is there any alternative to the language you used?

      • Ah, the code is in Ruby. Naturally, you can use any language you like — the key is that the “@” variables are those that are available to the page. Those are setting up the data that the page will need to render the charts. So long as you can gather the data and send it to the page, then you should be fine.

    • Thanks! I haven’t played with this stuff in a little while, but that will be handy when I get back to it.

  4. Pranav said:

    I was trying to put the use this code to make bar chart and was not able to to put the labels under the bars. Here is the piece of code I am using:

    window.onload = function () {
    var r = Raphael(document.getElementById(‘yieldPieChart’),500,500),
    fin = function(){
    this.flag = r.g.popup(this.bar.x,this.bar.y, this.bar.value || “0”).insertBefore(this);
    },
    fout = function(){
    this.flag.animate({opacity: 0}, 300, function() {
    this.remove();
    });
    };
    r.g.txtattr.font = “12px ‘Fontin Sans’, Fontin-Sans, sans-serif”;
    r.g.text(150, 100, “Yield”).attr({“font-size”: 20});
    r.g.barchart(30,30,400,320,[[696082531,7835000,7835000]]).hover(fin, fout).label([[2,52,53]]);
    };

    any suggestion please….

  5. Dave Webb said:

    A very interest page which I wish i had found earlier. IOt would have saved my a lot of time. Just wondering with the pie chart as to why both a slice and a legend value of ‘Others’ is being displayed?

    I to have this issue when populating the data from PHP arrays.

    Would very much appreciate a work around if you have one as this issue makes my chart unusable.

    Many thanks.

    Dave

  6. Kevin Jhangiani said:

    I just started experimenting with g.raphael, and have come to the same conclusion as you that it’s fantastic. I was having the same problem with putting numbers into the labels, but after looking at the source, I found there is a way to easily insert the values into the labels.

    In case you haven’t figured it out yet, you can put ‘%%.%%’ into the label, and this will display 30.45% in the label string. You can alter the format as you need (ie %% for no decimal points). You can also put ##, and this will insert the value of that pie segment into the label.

    • Thanks for the note. To be honest I’ve been busy with other things and haven’t played with raphael recently, but the label format info is useful, thanks. Recently I’ve been more occupied with using the D3.js library, which isn’t as flexible and powerful overall at raphael but can get to some very interesting things more quickly (at least for me). Both libraries are making charts so much better…

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: