Skip to main content Skip to footer

Rethinking the Periodic Table with Wijmo's Sunburst

Note: I wrote a follow-up blog post that expands on this one by covering some ways that Wijmo can help optimize your code. The sample link in this post now points to the sample for the new blog, but the visual result is identical.

Designed to ease the process of visualizing hierarchical data, sunburst charts display categorical data "tiers" followed by the data points themselves radiating out from a central point. To put it more simply, sunburst charts portray data as categorized arcs. Conveniently, Wijmo 5 now ships with a Sunburst control that makes it easy to visualize data as a sunburst chart on the web. In this blog, we'll adapt a .NET demo to display the periodic table of the elements as a sunburst chart on the web using only Wijmo's JavaScript Chart module and JavaScript (JS).

View the New Sunburst Periodic Table

Get the Sample on GitHub

Before we go any further, let's take a look at the finished product. Be sure to keep this in mind while moving through the tutorial so we can visualize the objective we want to reach.

An animation showcasing the final Periodic Sunburst data visualization's function

Now that we’ve visualized what we want our final to product to look like and do, let’s talk about why we would even want to do something like this and then how we can make it come to life.

What's Wrong with the Periodic Table?

Hopefully you’ve keyed into the mind of a data visualizer by now and are asking the question I stressed so much a little bit ago: why? There’s already a perfectly good, working periodic table of the elements out there, and many of us have been familiar with it for a good portion of our lives. So why change it? As a former biochemist, I can sympathize with anyone who’s trying to learn the periodic table for the first time or trying to _re_learn it as an adult. As we all know, the traditional structure isn't exactly the most intuitive. To a seasoned chemist or other basic science professional, the layout of the traditional periodic table makes perfect sense and is, in fact, very useful. To someone simply using it for reference or looking at it for the first time, however, its organization is often a mystery. So that's what's "wrong" with the traditional periodic table: it was designed for chemists, not teachers or students, or really anyone else for that matter. That's why Wijmo's Sunburst can help us here: it'll let us display elements grouped by their properties in a visually obvious way. A traditional periodic table designed for children with color coded groups. A traditional periodic table designed for children with color coded groups.

Building the Periodic Sunburst

_Before we get started, I want to point out that this tutorial doesn’t walk through every single step of the process. Although this is designed to be a complete project tutorial, our focus here is to demonstrate how we can link up datasets with the Wijmo Sunburst chart. If you want to view the complete project, it’s available on GitHub._

Step 1: Initializing the Project

As it goes in web development, there will be a near infinite amount of ways to set up this project. There are a number of frameworks, libraries and even programming languages that can be applied to solve this problem. That being said, I’m going to use vanilla (pure) JavaScript throughout the tutorial to make this transferable to any other JS-based solution. I also want to point out just how powerful and adaptable the Wijmo Sunburst control is. Look for notes pointing out sections where a framework or library would be useful, and rest assured that another tutorial focusing on building this project with a MVC framework is in the works. Next, I’ll talk about the project design considerations and how I approached them, but you can move on if you just want to check out the code. Aside from that consideration, there are a few other things to think about when setting up this project:

  1. We’re going to be loading JSON from a file. The easiest pure JS way to do this is by using an XMLHttpRequest (XHR). Since XHRs don’t work on the local file system, we’ll need a new way to test locally.
  2. We’re going to be writing a lot of data handling and access logic as well as view logic. I know, I know, JS isn’t technically object-oriented, but wouldn’t it be better to compartmentalize our code somehow?
  3. Since we want to walkthrough building a production-ready project from start to finish, how can we minify and package our files for production?

I thought about each of these issues and tried to come up with a solution that would leave the tutorial accessible to everyone while also providing a full-fledged, standalone solution. You can read more about the full details in the project README, but here are my solutions in brief:

  1. If you don’t have a development web server set up, I recommend using Apache. It’s quick, easy, and that’s what I’ll be using for all of my testing, just to allow XHRs to load the JSON data.
  2. I’ve decided to write the logic for this project using JavaScript modules. Specifically, I’m going to use CommonJS module syntax and patterns. Because of this, I’m also using Browserify to package all of the modules into a single JS file for production. Browserify is easiest to use with js and NPM, so I’ll also be using those as dependencies.
  3. Even though I’ll be writing code in vanilla JS, I use a lot of new ES6 syntax. To minify JS files, I’m using babili since it supports ES6.

So that’s pretty much it for my development dependencies! Again, you don’t have to use any of these, but I just wanted to give an overview of how I’m approaching things.

Step 2: Setting up the Data Source

To build out a new visualization of the periodic table, we’ll need data on the elements including their types so that we can correctly categorize them. Ultimately, we want this to be in JSON format so we can easily and natively work with and manipulate the data in JavaScript. Below, I’ll walk through the steps of downloading and cleaning the data from scratch, but if you want the final version you can grab it from GitHub.

  • Fortunately for us, Data Explorer offers a free dataset that is almost exactly what we need. The big caveat here is that Data Explorer provides their dataset in XML format with some extra schema information that we don’t need. Before we take care of that, though, let’s download the data.
  • Next we’ll convert the XML data we downloaded to JSON using a free online converter. Once we have the JSON representation of the data, we’ll save it in a file so we can clean things up a bit.
  • Now we have to trim away the excess schema information so we’re left with only a single root JSON entity that holds an array of element entities.
  • Lastly, believe it or not, some of the data provided by Data Explorer is little bit off! We’ll make some small corrections and save our final dataset.

Step 3: Building the Data Access Layer (DAL) and Data Model

Now that we have our cleaned-up data accessible to us, we have to build out the logic to load it and parse it. Just as it seems, this will require a two-step process in which we (1) load the JSON data from Step 2 into a JS object and (2) parse out the array of element data into formal Element objects and categorize them into Group and SubGroup objects as well. The second step can be broken down even further, as we have to develop a data model and an adapter to load data into it. Let’s start by building a utility to actually pull JSON from the file.


JsonDataLoader.prototype.getObjectFromJson = function (jsonFilePath, callback) {  
    readFileContents(jsonFilePath, function (eventArgs) {  
         callback(JSON.parse(this.responseText));  
    });  
};  

This is the most important function in the data loading module. It takes a file path and a callback function as arguments, and uses the below helper function to load the JSON then returns it as an object:



function readFileContents(filePath, callback) {  
    let reqClient = new XMLHttpRequest();  
    reqClient.onload = callback;  
    reqClient.open("get", filePath, true);  
    reqClient.send();  
}  


And that's it! We can now use these functions by requiring the JsonDataLoader module in any other code and using the getObjectFromJson function. If you want to see the full file contents and more documentation, follow the link up above. That goes for any code we talk about in the rest of the article, too! So now that we can load the data from a file, we need a model to store it in. Onto building the data model! Before we dive in, let's conceptualize what we need to build. Ultimately, we want (as the finished product picture showed) a model containing root "group" nodes which contain "subgroup" child nodes which contain elements as terminal nodes. Briefly, that equates to something like this: Periodic Sunburst Data Model Schematic A schematic representation of the data model necessary for building the Periodic Sunburst So how do we enact that structure in our code? It's probably easiest to just see it in action, so let's get to it!


var Element = function (elementJObj) {  
    this.atomicNumber = elementJObj.properties['atomic-number']; // this and atomic-weight must be accessed using bracket notation because they have hyphens  
    this.atomicWeight = elementJObj.properties['atomic-weight']; // this and atomic-weight must be accessed using bracket notation because they have hyphens  
    this.elementName = elementJObj.properties.element;  
    this.eleSymbol = elementJObj.properties.symbol; // rename this to eleSymbol since symbol is a reserved keyword in ES6  
    this.type = elementJObj.properties.type;  
    this.value = 1; // Set a uniform value that can be bound when initializing the chart to get equal arc angles  
};  

In Element.js, we just want to build a constructor that can take an element entity right from the JSON object and convert it into something more useful. It's also important to note the final property setter:

this.value = 1

This gives each Element object a property equal to the same numeric value which will be important later for rendering our JavaScript Chart.


var SubGroup = function (subGroupName) {  
    this.subGroupName = subGroupName;  
    this.elements = [];  
    this.characteristics = '';  
};  

In SubGroup.js, all that really matters is that we store a name property and set up an empty array that will ultimately hold Element objects. One "bonus" property that we set up here is the characteristics property. Eventually, this will hold a string representation of characteristics that we can display to the user.


var Group = function (groupName) {  
    this.groupName = groupName;  
    this.subGroups = [];  
};  

Even simpler than the SubGroup module, Group.js simply holds a constructor that takes a name and sets up an empty array for holding SubGroups. The data model is all written and assembled now, we just have to load data into it! The last step is to write a data adapter that can take the raw data loaded from JSON and put it into the model we just created.

As it would be a little overwhelming to post the entire DataSourceAdapter module here, follow the link above to view the documented code on Github. The primary function in this module is

DataSourceAdapter.prototype.getChartDataSource = function (callback) { ... }

which loads the element data using our JsonDataLoader utility module, parses it, and returns an array of Group objects. I highly recommend taking a look at the module to see exactly how the final data structure is generated, but the important result is that we are given an array of Groups which each contain SubGroups which each contain Elements. This can be directly loaded by our Sunburst chart in the view. And with that, we have a working data layer that can interface directly with our Sunburst chart!

Step 4: Building the View and Displaying the Sunburst Chart

The first step in displaying our Sunburst Chart in the view is by giving it a DOM host element in index.html:





This gives us a DOM element to render the chart in, but it doesn't actually handle any of the rendering. To generate the chart and place it in the element, we'll initialize the control in the code-behind, contained in app.js:


DataSourceAdapter.getChartDataSource(function (dataArray) {  
let mySunburst = new wijmo.chart.hierarchical.Sunburst('#periodic-sunburst'); // initialize!  
    // Let the Sunburst Chart know we're going to start making changes  
    mySunburst.beginUpdate();  

    // Set some stylistic properties for the chart  
    mySunburst.legend.position = 'None'; // hide the legend  
    mySunburst.innerRadius = 0.3; // set up a relatively large space for displaying info  
    mySunburst.selectionMode = 'Point';  
    mySunburst.dataLabel.position = 'Center'; // center panel labels  
    mySunburst.dataLabel.content = '{name}'; // the panel should display its name (derived from bindingName property)  

    mySunburst.itemsSource = dataArray; // set the items source to the array generated by the DataSourceAdapter  
    mySunburst.binding = 'value'; // bind each item to the constant 'value' property to get equal arc angles for all element panels  
    mySunburst.bindingName = ['groupName', 'subGroupName', 'eleSymbol']; // set the property that gives the name we want to display for each chart level  
    mySunburst.childItemsPath = ['subGroups', 'elements']; // set the property names that hold the children for each level (remember our data structure is Group.subGroups.elements)  

    mySunburst.endUpdate();  
...  
}  

The documented code above details the methods and properties we need to use to set up our JavaScript Chart to get the final product we're looking for. Especially important properties to notice are the itemsSource, binding, bindingName, and childItemsPath properties. These properties load the data we generated and formatted in Step 3, bind the arc sizing property for element panels to the uniform 'value' property, give the property names that the chart will look for to display data labels, and provide the property names for the arrays holding children, respectively. That alone should be enough to initialize the Sunburst chart and render it with all of the data we formatted into our model. But that's really only half the job, right? If you think back to the demo we conceptualized earlier, we also want our Sunburst to display information about the selected object in the center. So how do we go about doing this? Just like with everything else, let's break it down into steps: 1. Handle click events on the chart (app.js)


    // Set up a function to listen for click events on the Sunburst Chart's parent DOM element  
    mySunburst.hostElement.addEventListener('click', function (e) {  
        // Perform a hit test to get a clicked panel's name then use it to set up the info panel via the ViewAdapter  
        let ht = mySunburst.hitTest(e.pageX, e.pageY);  
        myPropTile.showInfoPanel(ViewAdapter.getObjectFromChartName(ht.name, mySunburst.collectionView));  
    });  

When the chart's host DOM element is clicked, we'll use the handy built-in Sunburst method of hitTest() to find out the name of the panel that was clicked. Next, we pass this name and the Sunburst's collectionView to a function in the ViewAdapter module that maps the name to the proper Group, SubGroup or Element object. Finally, we get an object representation of what was clicked, and can use that to show the properties we're looking for. 2. Send the clicked Object to myPropTile and show the properties (lib/view/PropertiesTile.js) So what exactly _is_ myPropTile? It's simply an instance of the PropertiesTile object wrapped around a DOM element. The PropertiesTile module contains an important function, showInfoPanel that initializes and displays the correct property tile. Once the PropertiesTile is instantiated and hooked up to the DOM events we need to listen on, we're done! There are quite a few intricacies around how the PropertiesTile works, and I encourage you to check them out in the source code.


That about does it for all of the core functionality of our data visualization! As you'll find in the GitHub repo, there are some additional steps taken that aren't covered in-depth here. These include everything from creating a shim for browser compatibility to theme generation with CSS. I encourage you to take a look at the source code holistically to get a better understanding of the overall project. If you have any questions about this project or the Wijmo Sunburst Chart in general, don't hesitate to let me know in the comments or by sending me an email. I'll be actively and openly responding to all feedback, so don't be shy! Thanks so much for reading, and I hope you enjoy! Download the source code for this sample: v0.1.1 Release (zip / tar)

View the New Sunburst Periodic Table

MESCIUS inc.

comments powered by Disqus