Quality Map using D3.js

Scenario

Draw a heat-map-like graphic using D3.js and bind click-event on radios using jQuery. This followings are the features.

  • A dropdown list to switch in several years (2014, 2015, 2016).
  • Each year will be a single layer and the layers will be switched by the dropdown list.
  • Each square of each layer will display a color in several RGBs.
  • Each square size is 5 x 5 pixels.
  • Around 100 x 100 squares.
  • A series of radios to display/hide min, max, average, etc..

2016-05-31_164017

DEMO SOURCE

Solution

Before We Begin

We have to import the libraries: D3.js & jQuery. And create a script block for our own program.

<!DOCTYPE>
<html>
  <head>
    <script type="text/javascript" src="https://code.jquery.com/jquery-1.11.3.js"></script>
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js"></script>
    <script type="text/javascript">
      // Our own program.
    </script>
    ...
  </head>
  <body>
    ...
  </body>
</html>

New Config Object

This Config includes some properties as below:

  • width[Number]: width of SVG
  • height[Number]: height of SVG
  • grid[Object]: Each grid(square) size: width and height.
  • diff[Number]: The gap of the sequence(array) of the data
  • colors[Array]: It arrayed from deep to light and uses HEX colors.

    var config = {
      width: 500, // SVG width.
      height: 500, // SVG height.
      grid: {
        width: 50, // Can divide SVG width,
        height: 50 // Can divide SVG height
      },
      diff: 200, // The difference(gap) between each color.
      // It arrayed from deep to light and uses HEX colors.
      colors: ['#993404', '#d95f0e', '#fe9929', '#fed98e', '#ffffd4']
    };

Create Functions

We need a function to get a position(x-axis, y-axis) from the index of data array.

    function getPosition(dataIndex) { // index is from 0.
      if(!dataIndex) return {x: 0, y: 0};

      var nthColumn = dataIndex % (config.height / config.grid.height), // x-axis: 0 - 99
          nthRow = Math.floor(dataIndex / (config.width / config.grid.width)); // y-axis: 0 - 99

      var columnWidth = config.grid.width,
          rowHeight = config.grid.height;
      return {
        x: columnWidth * nthColumn, // Translate columnWidth pixel nthColumn times
horizontally.
        y: rowHeight * nthRow // Translate rowHeight pixel nthRow times vertically.
      };
    }

And we have to create a function to get the grid background by the data value and config.diff.

    function getColor(dataValue) {
      return config.colors[Math.floor(dataValue / config.diff)];
    }

Let’s Draw!

First of all, color your border on SVG using CSS. Append the style to head tag.

<!DOCTYPE>
<html>
  <head>
    ...
<style>
      svg {
        border: 3px solid;
      }
    </style>

  </head>
  <body>
    ...
  </body>
</html>

And then, create SVG element as following:

    function renderSVG(data) {
      var svg = d3.select('body')
                  .append('svg')
                  .attr({
                    width: config.width,
                    height: config.height
                  });
    }

Now, append the grids to the SVG:

    function renderSVG(data) {
      var svg = d3.select('body')
                  .append('svg')
                  .attr({
                    width: config.width,
                    height: config.height
                  });
      svg.selectAll('rect') // Create virtual rect elements.
         .data(data).enter() // Deal with on each data, or put data in to the rect elemetns.
         .append('rect').attr({ // Assign every attribute we need.
           width: config.grid.width, // The width of each rect element.
           height: config.grid.height, // The height of each rect element.
           x: function(d, i) { return getPosition(i).x; }, // Assign the x-axis position to x attribute.
           y: function(d, i) { return getPosition(i).y; }, // Assign the y-axis position to y attribute.
           'fill': function(d) { return getColor(d); } // Set the fill value as the return value of getColor(d).
         }
      );
    }

As you can see, function(d) { return something; } returns corresponding value of data list. For example, ‘fill’: function(d) { return getColor(d); } will assign getColor(d) to ‘fill’ property.

Make Transition

It’s quite easy to make transition on your SVG. Follow as below and you’ll get the result you want.

    function renderSVG(data) {
      var svg = d3.select('body')
                  .append('svg')
                  .attr({
                    width: config.width,
                    height: config.height
                  });
      svg.selectAll('rect')
         .data(data).enter()elemetns.
         .append('rect').attr({
           width: config.grid.width,
           height: config.grid.height,
           x: function(d, i) { return getPosition(i).x; },
           y: function(d, i) { return getPosition(i).y; },
           'fill': '#fff' // The initial background.
      }).transition().duration(1500).attr({ // The 1500 within duration() means millisecond.
        'fill': function(d) { return getColor(d); } // The color when transition has been finished.
      });

The transition will bring the color from #fff to getColor(d).
Let’s generate random data and render it!.

    // Just a template.
    var qualityData = {
      2014: {
        min: [],
        max: [],
        avg: []
      },
      2015: {
        min: [],
        max: [],
        avg: []
      },
      2016: {
        min: [],
        max: [],
        avg: []
      }
    };

    // This function is for DEMO.
    function dataRandom(dataLen, maxValue) {
      for(var i = 0; i < dataLen; dataLen; i++) {
        qualityData[2014].min.push(Math.floor(Math.random() * maxValue));
        qualityData[2014].max.push(Math.floor(Math.random() * maxValue));
        qualityData[2014].avg.push(Math.floor(Math.random() * maxValue));
        qualityData[2015].min.push(Math.floor(Math.random() * maxValue));
        qualityData[2015].max.push(Math.floor(Math.random() * maxValue));
        qualityData[2015].avg.push(Math.floor(Math.random() * maxValue));
        qualityData[2016].min.push(Math.floor(Math.random() * maxValue));
        qualityData[2016].max.push(Math.floor(Math.random() * maxValue));
        qualityData[2016].avg.push(Math.floor(Math.random() * maxValue));
      }
    }

    // Execute function with parameters.
    dataRandom(100, 1000);
    renderSVG(qualityData[2014][min]);

Bind Events

Create dorpdown list and radios.

  ...
  <body>
    <div style="margin-bottom: 10px">
      <select id="yearSelectorquot;>
        <option value=quot;2014quot;>2014</option><!--This will be the selected one.-->
        <option value=quot;2015quot;>2015</option>
        <option value=quot;2016quot;>2016</option>
      </select>
      <input type=quot;radioquot; name=quot;valueSelectorquot; value=quot;minquot; checked>Min. <!--This will be the selected one.-->
      <input type=quot;radioquot; name=quot;valueSelectorquot; value=quot;maxquot;>Max.
      <input type=quot;radioquot; name=quot;valueSelectorquot; value=quot;avgquot;>Avg.</div>
      ...
  </body>

Bind event using jQuery.

  renderSVG(qualityData[$('#yearSelector').val()][$('[name=valueSelector]:checked').val()]); // Initial, the first fire.

  $('#yearSelector, [name=valueSelector]').change(function() { // Actual binding.
      renderSVG(qualityData[$('#yearSelector').val()] [$('[name=valueSelector]:checked').val()]);
    });

Reference

Author: jsgao0

My name is Jia-Siang Gao. Or call me Anson. At present, I'm a full-stack web developer in Kinpo Group. I love to sovle problems of technology.

One thought on “Quality Map using D3.js”

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s