Archive

Tag Archives: raphaeljs

This afternoon I wanted to add another quick report to the system I’m building, and it was so easy that I thought I’d share some of the details. As I’ve written here before, I’m using the RaphaelJS library for simple charts, and it makes it very simple to create bar charts and pie charts. So I’ve already got a couple of those, using common code as I described in that earlier posting.

When I wanted to create a new chart, then, I knew I could leverage that. First, though, I needed to get at my data. What I was getting was essentially a list of categories, and the number of items in each category. Since this is stored in Redis, the items are key-value entries, and each category is a Set to which items belong. In this particular case, each item belongs to only one set.

So for the sake of an example, let’s say that we have books, divided into categories. We’ll store the books with keys like book:#:title and each category set will be called cat:name:

  • book:1234:title => “Technical Book”
  • book:5678:title => “Another Tech Book”
  • book:9012:title => “Gardening Book”
  • cat:technical => 1234, 5678
  • cat:gardening => 9012

So, for charting purposes, we want to get a list of the categories, and then for each one we want to fetch the number of books in it. In my reports_controller I have this:

  def books_by_category
    @chart = params[:chart] || "bar"
    @chart_title = "Books by Category"

    redis = Redis.new
    # Get the list of the categories, which are the labels for the graph
    keys = redis.keys("cat:*")

    # Now let's iterate through the categories and get the counts
    @labels = []
    @values = []
    keys.sort.each do |cat|
      count = redis.set_count(cat)
      @values << count
      cat_name = cat.scan(/cat:(.*)/)[0][0]
      if (@chart == 'pie')  # Need to add counts to the labels for pie charts
        cat_name << " (#{count})"
      end
      @labels << cat_name
    end
  end

First we get a Redis connection, and ask it for all of the category keys, using the pattern chosen: redis.keys("cat:*"). I want to stress something here: if you read the Redis docs (which of course you should, in depth) you’ll see that they say to never use this command in a production app! Obviously, if you have a lot of keys in the database, this is not a good command. In this particular case, I know that the database being used will not have too many keys, so I’m comfortable doing this — but be careful and make sure it’s okay for your case! If not, the solution is to create a new set that contains the names of all of the categories. Grab that set using SORT and work from there, which is simple. I also want to stress that, as with any reporting, if your data set grows (i.e. you start to have lots and lots of categories), you don’t want to run this frequently! Do the count occasionally and cache the results, create roll-up data from which to do your reporting, etc. This is a very simple case, but is a nice example of some tools.

Okay, so then we have the categories, and we iterate through them. For each, we get the count of entries using redis.set_count(cat). The redis-rb library aliases “set_count” to be the Redis command SCARD, which returns the cardinality of the set, i.e. the number of entries. We add that onto the @values array, and then create the category name by taking everything after “cat:” from the key. If we’re making a pie chart, we add the count to the labels, simply because I found that it’s very friendly that way. We add the category name to the labels array, and continue.

That’s pretty much it then! Using the previous reporting code, I just had to create a new view, which includes the partials I created before — the partials expect the @labels and @values arrays, so they’ll just graph whatever they get. Here’s the actual view:

<%= render :partial => "report_chart" %>

<br/><br/>

<h1>Reports : Books by Category</h1>
<%= render :partial => "report_links" %>

<br/><br/>

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

If you refer back to my earlier post about RaphaelJS graphing, the report_chart partial contains the Javascript to generate the chart. The report_links partial simply has code to create links to the various chart types for this data: pie, bar, and csv. The holder div is where the RaphaelJS Javascript will render the chart.

And that’s all there is to it. Thanks to the ease of Redis sets, getting the data sliced and diced as needed was extremely simple, and thanks to easy Javascript reporting from RaphaelJS, the plain old label/value charting couldn’t be much quicker.

While doing some simple bar charts using raphaelJS, I ended up having trouble because my labels, which I was placing at the bottom of the chart, were trying to go horizontally, and they were too long to fit. The sensible thing seemed to be to make the labels orient vertically under the bars, like so:

The labels shown above are fairly short, of course, but I have other bar charts with longer labels. As it turned out, trying to get them oriented vertically like this wasn’t straightforward. After making the call to create the bar chart I tried to modify the labels, but it involved making some assumptions about the chart’s data structure, which seemed unwise; and in any case it wasn’t quite working properly.

So, I ended up modifying the raphaelJS code, in the g.bar.js file. I added a new rotate parameter to the label() function which for now just rotates the labels 90 degrees if the parm is true. After more testing I’ll see if it’s useful enough to fix up nicely, in which case I’ll make it take the degrees as the parameter. The tricky part actually ended up being the positioning — if the label gets rotated, it needs to be moved, so the code calculates the length of the text and figures out how much to move it down in order to position it under the bar properly.

So now I just call the bar chart function like so:

bc = r.g.barchart(30, 30, 500, 400, [<%= @values.to_json -%>]).hover(fin, fout).label([<%= @labels.to_json -%>], true, true);

The last parameter sets rotate=true and the other boolean is for isBottom=true, which is the default value.

I forked the raphaelJS code on github, and pushed my changes to my repository, so if you find this useful, go for it: Github. Enjoy.

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.

Follow

Get every new post delivered to your Inbox.