Skip to content

vis4/datawrapper-d3-bubble-chart

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

16 Commits
 
 
 
 
 
 
 
 

Repository files navigation

Adding visualization modules to Datawrapper

This tutorial shows how to add new visualization modules to Datawrapper. In this example we are going to add a nice D3.js bubble chart so Datawrapper users can create them without writing a single line of code.

bubble chart

Additionally to reading this tutorial you can also check the commit history of this repository to follow the individual steps.

Creating the host plugin

In general, to extend Datawrapper with new features you need to create a plugin. Plugins can do a lot of things, and adding new visualizations is just one of them.

The plugins are stored inside the plugins folder, so we create a new folder d3-bubble-chart for our plugin.

Now you need to create the package.json file which provides some meta information about the plugin itself (very similar to NPM package.json). The attributes name and version are required. The name must be the same as the plugin folder name.

{
    "name": "d3-bubble-chart",
    "version": "1.0.0"
}

As our plugin wants to provide a new visualization we need to create the plugin class plugin.php. The plugin class will be loaded by Datawrapper and its init() function is invoked on every request.

<?php

class DatawrapperPlugin_D3BubbleChart extends DatawrapperPlugin {

    public function init(){
        // do some stuff here
    }
}

By now the plugin is already ready for installation. To do so you open a command line and run the following inside the Datawrapper root folder:

$ php scripts/plugin.php install d3-bubble-chart
Installed plugin d3-bubble-chart.

Register the visualization

To register the visualization we will execute a special core method DatawrapperVisualization::register. It takes two arguments: the plugin that provides the visualization ($this) and an array with the visualization meta data ($visMeta).

We start easy with just some basic meta information: the visualization id and the title that is displayed in the chart editor.

<?php

class DatawrapperPlugin_D3BubbleChart extends DatawrapperPlugin {

    public function init(){
        $visMeta = array(
            "id" => "bubble-chart",
            "title" => "Bubble Chart (d3)"
        );
        DatawrapperVisualization::register($this, $visMeta);
    }
}

Define the visual axes

At this point it's time to introduce the concept of axes we are using in Datawrapper. Axes (aka dimensions) are the visual properties that are later mapped to columns of the uploaded dataset. For example, an simple scatter plot would provide two axes for the x and y position of each dot, each of which would accept numerical values.

In case of our bubble chart we at least need two axes for the bubble size and a label to be displayed on each bubble. The label axis accepts text and date columns while the size accepts only numerical columns.

<?php

class DatawrapperPlugin_D3BubbleChart extends DatawrapperPlugin {

    public function init(){
        $visMeta = array(
            "id" => "bubble-chart",
            "title" => "Bubble Chart (d3)",
            "axes" => array(
                "label" => array(
                    "accepts" => array("text", "date")
                ),
                "size" => array(
                    "accepts" => array("number")
                )
            )
        );
        DatawrapperVisualization::register($this, $visMeta);
    }
}

Once the axes are defined Datawrapper will automatically assign data columns to them when a new chart is created. The first text or date column is assigned to the label axis, and the first number column is assigned to the size column.

Prepare the visualization JavaScript

At first we need to create the JavaScript file that is loaded with the chart. Like any plugin file that we want to be publicly accessible, we must be locate it in a sub-folder named static/.

It is important to name the file exactly after the visualization id you defined in the $visMeta array above, so in this case we would name it bubble-chart.js.

In the base skeleton for the file we simply call the framework function dw.visualization.register to register it's code. As first argument we pass the visualization id and last second argument is an object with a function render().

dw.visualization.register('bubble-chart', {

    render: function($element, dataset, axes, theme) {
        // render the visualization inside $element
    }

});

And this render function is where all our code will go into.

Prepare the dataset

Now it is the time where the actual fun starts, as we are switching to JavaScript to code our visualization. Let's begin with collecting and preparing the data.

The bubble chart code (that we adapted from this example) expects the data in a structure like this:

{
    "children": [
        { "label": "Bubble 1", "value": 123 },
        { "label": "Bubble 2", "value": 234 }
    ]
}

To access the chart's dataset we can use the references to the dataset and the chart axes.

render: function($element, dataset, axes, theme) {
    // create the empty structure
    var data = { children: [] };
    // loop over each row in our dataset
    dataset.eachRow(function(i) {
        // append new objects for each row
        // with the values from the axes
        data.children.push({
            label: axes.label.val(i),
            value: axes.size.val(i)
        });
    });
}

To see if this works we can output the data using console.log. At this point it's a good idea to actually create a chart with some test data so we can test our code.

console output

Code the visualization!

To code the visualization we start by adapting the bubble chart example kindly provided by Mike Bostock. One thing we have to change is the selector to which we append the svg element. Instead of selecting the body we will use the $element instance (which happens to be a jQuery selector).

var vis = d3.select($element.get(0)).append("svg")
    .attr("width", diameter)
    .attr("height", diameter)
    .attr("class", "bubble");

The other thing we change is the data. In our case we don't need to load an external JSON file so we don't need to wrap the visualization code inside a d3.json call. Also we don't use the d3.scale.category10 palette yet as all our circles will have the same color.

The full code at this point can be found here. However, if we test the chart in Datawrapper we will experience an error saying: Uncaught ReferenceError: d3 is not defined. Obviously we yet need to tell Datawrapper that our visualization depends on the third-party library D3.js.

Declaring dependencies to third-party libraries

To do so we do two things: First we download d3.min.js and store it under static/vendor/.

Second we need to tell Datawrapper that it needs to load the library with the chart. Therefor we add the new attribute libraries to the visualization meta data we define in plugin.php. For each library we can provide two URLs, a local URL that is used in the Datawrapepr editor and a remote URL that will be used in the published chart.

"libraries" => array(array(
    "local" => "vendor/d3.min.js",
    "cdn" => "//cdnjs.cloudflare.com/ajax/libs/d3/3.3.11/d3.min.js"
)),

After fixing the dependency the resulting chart should look something like this:

output

Fitting the visualization into the chart

You might have noticed that at this point the visualization uses a fixed size, which is not what we want in Datawrapper. Instead we will call this.size() to get the width and height available for the chart and use the smallest side as diameter.

var size = this.size(),  // returns array [width, height]
    diameter = Math.min(size[0], size[1]);

Using the theme colors

Next thing we do is to re-use the colors defined in the currently selected theme, so our bubble chart will fit into the design. So instead of the fixed color "#ccc" we are going to take the first color of the theme's palette.

node.append("circle")
    .attr("r", function(d) { return d.r; })
    .style("fill", theme.colors.palette[0]);

To ensure that the labels remain readable it's a good idea to check the Lab-lightness of the selected color.

Putting stylesheets into separate file

At this point our code is already infiltrated with style definitions that would better fit into a separate CSS file. Datawrapper makes this very easy by automatically including the CSS files named after the visualization id. So in our case we simply add a file bubble-chart.css into the static/ folder and that's it.

Then we can remove the .style() call and instead use a CSS class to invert the labels.

By now the visualization looks something like this:

output

Make the visualization customizable

As you know, the visualizations in Datawrapper usually can be customized by the user in some ways. Therefor the visualization can define a set of options that will be rendered as UI controls in the chart editor.

Let's say we want to allow users to turn off the bubble labels. All we need to do is to add the following line to the visualization meta definition and wrap the code that renders the labels in an IF-statement. To get the current setting we can use this.get('show-labels').

"options" => array(
    "show-labels" => array(
        "type" => "checkbox",
        "label" => "Show bubble labels",
        "default" => true
    )
)

Now the sidebar we see our new checkbox, and after unchecking it the labels disappear.

output

Allow highlighting of elements

Another nice feature of Datawrapper is the ability to highlight certain elements of a visualization. This is very easy to integrate into a visualization using two little steps.

At first we need to define which of our axes is storing the unique identifiers in our dataset. In our case this is the axis label, so we set the attribute highlight-key to "label" in the visualization meta attributes.

As second we need to alter the visualization code to check if a given element is highlighted, and then change the appearance accordingly. To make it easy we just change the default opacity of the circles to 50% and resetting it to 100% for all highlighted elements. All this can be done in CSS:

.node circle { opacity: 0.5; }
.node.highlighted circle { opacity: 1; }

Next we need to assign the class "highlighted" to all highlighted elements. To do so we can use the function chart.isHighlighted(key). Note that if no element is highlighted at all this function will return true for all elements.

And again, that's it. Now we can select elements in the chart editor and the remaining elements will be faded out a little bit:

output

Don't stop here

For this tutorial this should be enough to demonstrate how you can add new visualization modules to Datawrapper. As you have seen, the integration of an existing D3.js visualization is pretty straight-forward, and can be done within a few minutes.

However, while what we achieved so far is certainly nice, a lot more work is needed to get the bubble chart to Datawrapper chart quality. This is something that is easy to be forgotten: a good chart consists of way more than just a few bubbles and some text.

To give a few ideas about what is missing:

  • The actual values are not displayed in the bubble chart. A radius legend or direct labeling of large bubbles would help!
  • Simply truncating labels according to the radius is not very smart. It would be better to let the labels break into several lines.
  • We could allow users to customize the colors for the bubbles, either using the built-in color picker in Datawrapper or by adding another axis for data-driven coloring (as is it used in the map module).
  • Finally, we could improve backward compatibility by using Raphael.js or plain HTML instead of SVG.

About

This plugin demonstrates how to add new visualizations to Datawrapper

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published