Archive

Tag Archives: graphs

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.