Create new chart types

38d87d9852a66eea29da00ae16511664

Updated at October 15th, 2019

Protobi is a lightweight library to visualize survey data in your browser. Protobi includes a number of standard chart types, but also allows you to create your own charts.

So if you're familiar with web programming, this tutorial is for you. Protobi's API is open, and the example below shows how you how to extend it.

And in any case we're happy to create custom widgets for your specific project and we have a growing library of general charts you can import and extend.

Libraries included

Protobi includes leading visualization libraries d3.js and Plotly.js as well as utility libs like lodash.js , Backbone.js and jQuery.

You can include additional libs and stylesheets, as shown below.

Example

Here we'll create a new chart type that uses The Wall Street Journal's Squaire algorithm to displays state data geographically as a pseudo-map with neat squares:


The WSJ Squaire chart assumes an input variable with two-character state abbreviations. For this example we have a survey with a variable s3 with exactly this: 


Define a minimal chart

In the Admin tab of the project, create a new document called squaire.js. Then press "Edit/Run..." and paste the following code there and press "Save."

This is the custom code that defines our new chart. The code below is boilerplate that extends Protobi's BasicElement to define a new SquaireChart.

You don't really need to read it in great detail, just note that there are two methods that your code will override:

  • preRender: function(callback) {}
  • render: function() {}`

The method preRender is where you'll put background calculations or code should run asynchronously in the background before rendering.

The method render is where you'll actually draw the visualization. Specifically, you'll render a DOM element referenced by this.$content.

Boilerplate

$(function() {
$(".protobi.app").on("protobi-loaded", function() {
var BasicElement = protobi.widgetFactory.getView('BasicElement')

  var SquaireChart = BasicElement.extend({
    alias: "Squaire",
    className: 'element resizable squaire',
    defaultOptions: {},
    moreEvents: {},
    resizable: true,
    showMargin: false,

    events: function () {
      var parentEvents = BasicElement.prototype.events
      if (_.isFunction(parentEvents)) {
         parentEvents = BasicElement.prototype.events();
      }
      return _.extend({}, parentEvents, this.moreEvents);
    },

    renderChildren: function () {
      return false
    },

    postInitialize: function (args) {
      var self = this;
      SquaireChart.__super__.postInitialize.apply(this, arguments);
      this.listenTo(this.tabular, 'filter', self.update)
      this.listenTo(this.model, 'change', self.update)
      this.listenTo(this.viewModel, 'change:format', this.update)
    },

    preRender: function (callback) { 

      // ... add your calculation code here ...

      return callback(null, this.data)
    },

    renderBody: function () {
      // ... add your rendering code here...
      this.$content.html("Hello World!!);

      return;
    }
  })

  protobi.widgetFactory.registerView('SquaireChart', SquaireChart)
});
})


Protobi uses Backbone.js to define charts. The above code extends the basic chart type to create a new one called "SquaireChart":

Apply the new chart

You can now apply this new chart type to any element. Create a new element. Select "Edit JSON..." and set "chartType": "SquaireChart":


Voilá, you now have a new chart that uses this code to render the element.
This doesn't do anything special yet but it will...

Define data prep

Do any calculations in the method preRender. This method is called in the background before rendering any time the element or filters change.

Here we'll just calculate the distribution of this element given its key and field attributes. Replace the method preRender as shown below:

    preRender: function (callback) {
      "use strict";
      var self = this;

      this.table = this.tabular.getDistribution(this.model, ['current']);

      return callback(null, this.data)

    },

Include external libraries your code requires

In the Admin tab of the project, create a new process called header. This will allow you to specify any additional resources your project requires. Then press "Edit/Run..." and paste the following code there and press "Save."

At the top include any external library calls your method requires. Here we'll call the WSJ Squaire library directly from GitHub using jsDelivr:

<script type="text/javascript" src="https://app.protobi.com/v3/datasets/5c1804033e40da00044ff56f/direct/squaire.js"></script>  
<script type="text/javascript" src="https://cdn.jsdelivr.net/gh/WSJ/squaire/src/js/squaire.js"></script>  

In the example above replace 5c1804033e40da00044ff56f with your project ID which can be found in the URL.

Render the chart

Replace the line // ... add your rendering code here... with code that draws the chart as you would like to see it. Add any content to a DOM element defined by the variable this.$content.

In this example we'll adds two new <div> elements that Squaire expects, and then calls the Squaire library to render them:

Example

 renderBody: function () {
      "use strict";


      var self = this;
      if (!self.table) return;
      self.$content.empty()
      var chartOptions = self.getChartOptions();
      var showMissing = this.getShowMissing()

      var mapContainer = $("<div>")
        .addClass('map-container')
        .appendTo(this.$content)

      var tipContainer = $("<div>")
        .addClass('squaire-toolbox')
        .appendTo(this.$content)

      var data = {}
      this.table.getRowKeys().map(function (rowKey) {
        var freq = self.table.getFreq(
         'current', 
          rowKey, 
          showMissing
        )
        data[rowKey] = {
          "value": freq
        }
      })

      var map = new Squaire(data,
          {
            colors: d3.scale
              .quantize()
              .domain([1, 5])
              .range(['#c9e2f5', '#0098db']),
            el: mapContainer[0]
          });

      return;
    }


Result

We now have an interactive chart that displays frequencies by state as a stylized US map, and this chart type can be applied to multiple elements.

Cheat

If you want this specific chart in your project, you don't need to do all the steps above. You can include the following in your header code and include code we've already written. These will define two new chartType values, "Squaire" and "MapUSStates".

Protobi has a growing library of advanced charts, and we're working on better ways to share these across projects. But for now just paste these into your header.

<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/WSJ/squaire/dist/squaire.css"/>
<script type="text/javascript" src="https://cdn.jsdelivr.net/gh/WSJ/squaire/src/js/squaire.js"></script>
<script type='text/javascript' src="https://app.protobi.com/v3/datasets/5c23a0bf28bd890004edb591/direct/d3-tip.js"></script>
<script type='text/javascript' src="https://app.protobi.com/v3/datasets/5c23a0bf28bd890004edb591/direct/squaire.js"></script>

<script src="//cdnjs.cloudflare.com/ajax/libs/topojson/1.1.0/topojson.min.js"></script>
<script type='text/javascript' src="https://app.protobi.com/v3/datasets/5c23a0bf28bd890004edb591/direct/map-us-states.js"></script>

Closing note

If you're working your way through this tutorial, yay you! This is an advanced topic. You're welcome to contact us support@protobi.com and we'll help you get up to speed.

Was this article helpful?