Anna Pawlicka

Programmer. Hiker. Cook. Always looking for interesting problems to solve.


About | Archive | Talks

Pretty charts with dimple.js

21 Jul 2013 | chart, dimple.js, JavaScript

I have spent half a day trying to create a bar chart with d3 library. It’s been quite painful so I could not have been happier when I learned about dimple.js. Dimple is powered by d3, but it does not overwhelm the user with the typical complexity of d3. A simple bar chart can be created in just a few minutes! Have a look below for the step by step example of how to create and customise a chart.

Load data and create simple bar chart

First we need to create a new svg container, specifying its dimensions and div in which it will be placed. Then we load our data. The dataset used in this example is a csv file, but d3 library that works underneath allows for all sorts of files (tsv, xls, etc.). We need to set bounds of the chart, and specify what data is to be added to x- and y-axis.

<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://dimplejs.org/dist/dimple.v2.1.2.min.js"></script>
<script type="text/javascript">
  var svg = dimple.newSvg("#chartContainer", 490, 430);
  d3.csv("data/Birds.csv", function(data) {
      var myChart = new dimple.chart(svg, data);
      myChart.setBounds(60, 10, 400, 330)
      var x = myChart.addCategoryAxis("x", ["SPECIES"]);
      myChart.addMeasureAxis("y", "D/S");
      var s = myChart.addSeries("SPECIES", dimple.plot.bar);
      s.barGap = 0.05;
      myChart.draw();
  });
</script>

Dimple takes care of all the rest and creates a nice bar chart with a tiny tooltip:

Customised tooltip

If we want to add more information into the tooltip, or we want additional formatting (e.g. rounding of numbers), we can create our own tooltip. For that we need our own event handlers, coordinates of where the popup should be displayed, and a group for the popup rectangle and the text. We also need to specify the attributes and style. Here’s the code snippet:

<script src="http://d3js.org/d3.v3.min.js"</script>
<script src="http://dimplejs.org/dist/dimple.v2.1.2.min.js"></script>
<script type="text/javascript"/>
var svg = dimple.newSvg("#chartContainer2", 490, 430);
d3.csv("../public/data/Birds.csv", function(data) {
    var myChart = new dimple.chart(svg, data);
    myChart.setBounds(60, 20, 400, 330)
    var x = myChart.addCategoryAxis("x", ["SPECIES"]);
    myChart.addMeasureAxis("y", "D/S");
    var s = myChart.addSeries(["SPECIES"], dimple.plot.bar);
    s.barGap = 0.05;

    // Handle the hover event - overriding the default behaviour
    s.addEventHandler("mouseover", onHover);
    // Handle the leave event - overriding the default behaviour
    s.addEventHandler("mouseleave", onLeave);

    myChart.draw();

    // Event to handle mouse enter
    function onHover(e) {

        // Get the properties of the selected shape
        var cx = parseFloat(e.selectedShape.attr("x")),
            cy = parseFloat(e.selectedShape.attr("y"));

        // Set the size and position of the popup
        var width = 150,
            height = 70,
            x = (cx + width + 10 & lt; svg.attr("width") ?
                cx + 10 :
                cx - width - 20);
        y = (cy - height / 2 & lt; 0 ?
            15 :
            cy - height / 2);

        // Create a group for the popup objects
        popup = svg.append("g");

        // Add a rectangle surrounding the text
        popup
            .append("rect")
            .attr("x", x + 5)
            .attr("y", y - 5)
            .attr("width", 150)
            .attr("height", height)
            .attr("rx", 5)
            .attr("ry", 5)
            .style("fill", 'white')
            .style("stroke", 'black')
            .style("stroke-width", 2);

        // Add multiple lines of text
        popup
            .append('text')
            .attr('x', x + 10)
            .attr('y', y + 10)
            .append('tspan')
            .attr('x', x + 10)
            .attr('y', y + 20)
            .text('Species: ' + e.seriesValue[0])
            .style("font-family", "sans-serif")
            .style("font-size", 10)
            .append('tspan')
            .attr('x', x + 10)
            .attr('y', y + 40)
            .text('Dive to Surface Ratio: ' + Math.round(e.yValue * 10) / 10)
            .style("font-family", "sans-serif")
            .style("font-size", 10)
    }

    // Event to handle mouse exit
    function onLeave(e) {
        // Remove the popup
        if (popup !== null) {
            popup.remove();
        }
    };
});
</script>

The final bar chart with a customised tooltip is shown below.


Customise axis label and position

// Override x-axis title
x.titleShape.text("My own title");
// Title is placed below the tick labels by default. This overrides this setting and places it immediately below the axis.
x.titleShape.attr("y", myChart.height + 55);

Change default colours

Colours of bars can be changed via CSS:

rect {
    stroke: #41B6C4;
    fill: #41B6C4;
}

CSS overrides default colours of bars or circles. We can also change colours by changing the default values:

// Override tooltip colour
myChart.defaultColors = [
 new dimple.color("#E0FFFF")
];

There are many more ways in which the charts can be customised, and if we feel the need to dig deeper, d3 objects are accessible and ready for us to mess with them.

For dimple API visit its Wiki pages.

[CSV dataset source: Simon’s Discoveries]


Older · View Archive (25)

Clojure is addictive (even though it’s breaking my brain)

I’m currently reading Clojure in Action, and going through 4clojure exercises. I have also set up a github repo for my solutions. Feel free to suggest better solutions!

Newer

How to process (small) dataset with Cascalog

My learning at university involved processing Twitter dataset using Hadoop cluster (as part of a very useful module called High Performance Computing). All done using pure Java. These are not good memories. Jobs failing, jobs queuing, cluster experiencing downtime, students panicking, staff complaining. Not to mention the need to write my own Mapper, Reducer, Combiner and a job configuration. And the job was really tiny! Oh, and the need to create a new jar and copy it to hdfs each time I changed the code. If only I could just write a simple query and run it the way SQL is run..

One word: Cascalog.