Treemap on Rails 8 comments

Posted by robon July 27, 2006

You may have used the Active Record “acts_as” extensions that ship with Rails, such as acts_as_list, or those added by third-party plugins, such as acts_as_attachment. In this post I’m going to cover how to use a new plugin for Rails by Andrew Bruno, called acts_as_treemap.

What is a Treemap?

A Treemap is a diagram that allows you to easily visualize hierarchical information, or trees. The first treemap was used to visualize the directory structure of a filesystem; to make it very easy to identify the disk-hogs (files taking up a disproportionate amount of disk space) on a system with very limited resources. A more recent example of a treemap is one that Tim O’Reilly has posted on O’Reilly’s Radar that shows recent trends in programming language book sales.

The following treemap show two dimensions of information: Square size represents sales volume, and color represents rate of growth.

Book Sales

In order to represend your data with a treemap, your data must be modeled as a tree. The tree data model is much like that of an XML document, where there’s a root or parent node, and zero or more child nodes. Each subsequent node may have it’s own children, and so on.

In SQL, this structure is simple to set up. You create a field that serves a the primary key (in Rails that’s always “id”), and another field that stores the parent id of each record (e.g. “parent_id”). Note that the root node will have a parent_id of “NULL.” There can be more fields, of course, but these are all that are required to structure records as a tree.

The following diagram shows the relationship between data in a table, a tree structure, and a treemap:

Tree Structure

Using acts_as_treemap with Rails

To demonstrate how to use the acts_as_treemap plugin Andy has supplied an interesting dataset; a database of SourceForge projects. The information the resultant treemap will convey has to do with project activity; number of downloads and rate of change (ROC). With such a graphical representation of this data, it becomes very clear what projects are on the rise or decline, and the popularity (number of downloads) of projects relative to one another.

Consider this example merely a starting point for your own implementation. There are a number of built-in options you can set to alter the properties and appearance of the treemap, but there are endless other possibilities as well. For example, you could have JavaScript pop-ups over each square that display even more details about each segment of data.

Step 1. The first step is to download and install the ruby-treemap gem. This gem is deliberately created separately from the Rails plugin so that it may be extended for any Ruby client that needs to build a treemap, not just Rails. Install the ruby-treemap gem with:

$ sudo gem install ruby-treemap

Step 2. The SourceForge sample data can be downloaded from here. The only table that we’ll be using for this example is sourceforge_nodes.

$ wget http://www.qnot.org/sourceforge_database.sql 

Step 3. You’ll need to create a database to load the SourceForge data into. Call it “sourceforge” and grant your Rails application user (e.g. “rails_user”) access to it. As long as you’re at it you should create a sourceforge_test database as well, although we won’t really be using it much here.

$ mysql sourceforge -u rails_user < sourceforge_database.sql

Step 4. With your database set up and loaded with data data you’re now ready to create your Rails application. Let’s call it “sfmap.” Create it now, with:

$ rails sfmap

Step 5. Now you need to configure the Rails app you just created so that it may communicate with your database. Do so with the following (replace username and password if needed):

./config/database.yml:
development:
  adapter: mysql
  database: sourceforge
  username: rails_user
  password: ******
  host: localhost
test:
  adapter: mysql
  database: sourceforge_test
  username: rails_user
  password: ******
  host: localhost
production:
  development

Step 6. Now you can install the acts_as_treemap plugin into your Rails application’s vender/plugins directory. This simple with the ./script/plugin command. Just type the following from the root of your sfmap project.

$ ./script/plugin install http://code.qnot.org/svn/projects/acts_as_treemap/

Step 7. Now you need to set up an Active Record model to represent the sourceforge_nodes table in the database. Generate it now by passing the name of this class (“SourceforgeNode”) to the Rails model generator:

$ ./script/generate model SourceforgeNode 

Step 8. Open up the model class file that was just generated, and replace its contents with the following:

./app/models/sourceforge_node.rb:
<samp>class SourceforgeNode < ActiveRecord::Base
  acts_as_tree :order => "curmo" 
  acts_as_treemap :label => "name", :size => "curmo", :color => "roc" 
end</samp>

You can see a little foreshadowing of our expected outcome with the two method calls: acts_as_tree and acts_as_treemap. They set up the model to be “treemapped” and configure what columns are to be represented by region size, and by color.

The first call to acts_as_tree is actually a build-in Rails method. It tells Rails to treat the data in this table as a tree structure (each row references a parent row, excluding the root element). The :order => “curmo” specified how to order children of the tree structure that are on the same level of the tree and would be otherwise unordered.

The acts_as_treemap method specifies to use the SourceForge project name for labeling each region of the treemap, the size of each region is to be based on the number of downloads for the current month, and the color of each region is to convey information about the rate of change in the number of downloads for each project.

Step 9. With the model configured, you can move on and generate a controller. Create a controller named “SourceforgeMap” and a corresponding “index” view using the controller generator.

$ ./script/generate controller SourceforgeMap index

Step 10. Now edit the controller that was just created and replace its contents with the following:

./app/controllers/sourceforge_map_controller.rb:

<samp>class SourceforgeMapController < ApplicationController
  def index
    # Retreive the root node of the treemap
    @root = SourceforgeNode.find(18)
  end
end</samp>

The call to SourceforgeNode#find above sets the id of the root element of the tree structure (the element of which all other elements are children of). (Id #18 in the SourceForge data represents “topic.”)

Step 11. In the index.rhtml view that was created with the controller generator above, add the following call to the html_treemap method, passing in the @root instance variable, containing the root level SourceforgeNode object.

./app/views/sourceforge_map/index.rhtml:
<%= html_treemap(@root) %>

Step 12. Finally, start up your server with

$ ./script/server

and view the treemap output in your browser (http://localhost:3000/sourceforge_map). You should see something like the following:

SourceForge Treemap

View an HTML version of the treemap with Javascript tool-tips here.

Once you’ve got this example up and running, you can switch out the SourceForge data for your own, and then modify or even extend the rendered treemap however you like. Oh, and if you end up using this plugin in your own projects, please report back about it in the comments.

Thank you,
Rob


Comments

Leave a response

  1. George HotellingJuly 27, 2006 @ 03:09 PM

    Awesome work. I can think of a couple places where I could use this.

    One nit though: according to MVC separation shouldn’t this be configured in the view or a helper, instead of the model? I suspect that I’m going to run into problems if I can only use one set of options per table.

  2. moeJuly 28, 2006 @ 01:09 AM

    Nice work. If you need flash treemap renderer (just in case) – feel free to use mine: http://src.moedusa.name/trac/treemap

    it was made relatively long ago and was not in active development since that time, bu it’s pretty OO Actionscrip. I had plans to mak js only renderers, but got no time.

    sorry fo my poor Eng.

  3. anrakeJuly 28, 2006 @ 02:14 AM

    Very cool! How about outputting a simple tree structure utilizing unordered lists?

  4. German RummJuly 28, 2006 @ 08:00 AM

    Nice tutorial! Liked it very much, can’t wait to try it. Started learning rubyonrails two days ago – never touched plugins yet.

    And, in step 11, <%= html_treemap(@root) > call cannot be seen without viewing HTML source code.

  5. RickJuly 28, 2006 @ 08:17 AM

    Ironically, O’Reilly’s “tree” isn’t tree data at all—merely 2-axis data that he forced into an arbitrary tree to generate a treemap.

    The second data is more tree-ish, due to sf.net’s project hierarchy, but IIRC (could be wrong) isn’t it possible for a project to appear in multiple places in the hierarchy? May be a DAG instead of a tree.

    Nice plugin though when it’s used properly.

  6. ozgur alazJuly 28, 2006 @ 10:18 AM

    Great tutorial Thanks I dugg the news Please add your diggs http://digg.com/programming/How_to_easily_visualize_hierarchical_information_on_Rails

  7. Matt BJuly 28, 2006 @ 10:17 PM

    SpaceMonger has always been my favourite use of a treemap. :-) http://www.sixty-five.cc/sm/scrnshot.php

  8. Rob OrsiniJuly 29, 2006 @ 10:24 AM

    @Rick – Thanks for the comment, but please have a look at this view of the O’Reilly Research treemap tool:

    http://radar.oreilly.com/archives/Q206pubtreemap-thumb.jpg

    In that image it’s more obvious that the bookscan sales data is, in fact, structured as a tree.