Making an Interactive Scatter Chart Visualisation [A How-To Guide]

Written by markupus | Published 2020/03/01
Tech Story Tags: creating-visualizations | d3-library-chart-plugins | making-chart-interactivity | data-loading-dom | trial-and-error-domain-range | build-responsive-visualization | open-source | hackernoon-top-story

TLDR A client wanted to give its users a more engaging way of browsing a range of products (in this case betting providers) A key driver was the idea of knowing ‘what’s good’ - really appreciating how a site's attributes stood up against all the alternative options. We chose a D3 library from the many chart plugins available because it offers the flexibility to customize and visualize as per requirement. D3 is an open-source library, so it enables users to work with the source code and add specific features.via the TL;DR App

A client wanted to give its users a more engaging way of browsing a range of products (in this case betting providers). A key driver was the idea of knowing ‘what’s good’ - really appreciating how a site’s attributes stood up against all the alternative options. Is a £30 bonus high or low?

Does having 11 payment methods put you in the top 10% or somewhere around the middle? It felt like a great chance to develop a new comparison paradigm that put relative performance at the fore, in an intuitive way. 
Thankfully, we had recently seen a fantastic example that inspired us from The Pudding, so we had a picture in our mind of how this could behave. But how did we build it? This post charts our progress through developing this feature and coming out with a pretty nice (we think) result.
TL;DR: after quite a lot of fiddling and testing, we produced this interactive & responsive chart, which satisfied both us and the client to the tune of around 100 hours front end development & testing time.

The right tool for the job

We choose a D3 library from the many chart plugins available because it offers the flexibility to customize and visualize as per requirement,
While most of the libraries are free and open source, some of them provide a paid version with additional features. For example, Highcharts is free for personal use, but you need to purchase a license for commercial usage. D3 is an open-source library, so it enables users to work with the source code and add specific features.
There are many libraries that render charts using HTML/CSS with SVG or CANVAS technology and come with various customization options, such as Highcharts, amCharts, Google Chart, CanvasJS, and jqChart. 
However, some of them are paid for while others don't provide the required functionality, such as preventing elements overlapping and causing elements to be attracted towards a specified position. In D3 library, the force-directed algorithm balances the node positions with the fewest iterations.
To summarize, the following advantages of the D3 library helped us to build the chart we wanted:
  • D3 is an open-source Javascript library.
  • it can be used with any JS framework. You don't need any other technology or plugin other than a browser to get started with D3.
  • D3 is extremely flexible. Developers have a lot of control over the way the chart renders. D3 supports a vast variety of chart types and has a rich toolset for data-driven visuals.
  • D3 loads or imports data from different types of documents, binds the data to the specific DOM, transforms or controls visual side of elements.
We used D3.js library to achieve our goal, taking advantage of its various modules (Arrays, Axes, Forces, Scales, Selections, Transitions etc).
We also extended D3’s source code to meet our own needs.

Chart interactivity

Our example shows a visualization of gambling operators sorted by a specific parameter (e.g. Wagering (bonus), Wagering (deposit), Bonus cap (£), Bonus %, Software Providers, Deposit Methods, Withdrawal Methods).
The chart has the following interactive features:
  • Clicking on a tab recalibrates the chart depending on the operators’ data
  • Clicking on filter by name displays defined operator(s)
  • Hovering over an operator displays a tooltip with data and a link to that operator

    Step by step

    The process was to implement several points step by step:
    1. Loading/reloading data based on which tab/metric the user selected
    2. Adding axis with ticks and labels
    3. Creating tooltips with data
    4. Filtering by tabs
    5. Filtering by name
    6. ForceSimulation
    7. Responsiveness

    Data loading

    Using D3.js we can manipulate with the Document Object Model (DOM). The D3 library provides numerous methods for transforming nodes: setting attributes or styles, registering event listeners, adding, removing or sorting nodes, and changing HTML or text content.
    To start with, D3.js loads data and binds it to DOM elements. D3 works with different types of files (css, tsv, json, xml etc). We chose a JSON file. D3.json() takes the JSON file as input and converts it into an array of objects. It uses the data() method and adds enter() method, which works kind of like a loop, then append() method which adds elements to the DOM.
    var circles = self.chart
        .append("div")
        .attr("class", "casino-circles")
        .data(filteredNodes)
        .enter()
        .append("div")
        .attr("class", "casino-circle")
        .attr("data-id", function(d) {
            return "casino-" + d.id;
        })
    

    Challenges

    One of the complex issues we had to overcome was the fact that the one chart comprised several different graphs with their own measurements, axis and data.
    We had to be attentive when adding axes to the chart as we had to map a dimension of abstract data to a visual representation.
    In other words, we need to take an abstract value of data (e.g. bonus cap in £) and return a visual value, such as the horizontal position of a dot in pixels.
    Let’s say that our input data Bonus cap £ will be some number between 20 and 600; we need to scale our data down to fit into the chart viewport.
    This is implemented by D3 scale functions.
    They map an input domain ([20, 600]) to an output range (in our case chart width). The Domain() method defines full chart boundaries. First parameter points to the minimum value in the data, the second one to the maximum value in the data.
    The Range() method will store our values inside our SVG container. This function is responsible for the physical boundaries of our chart. The first parameter gets value (0), the second one width/height of SVG container.

    Trial and error

    There were several difficult moments in the implementation. Some things were achieved by trial and error. In particular, force simulation.
    From
    And
    To
    The power and flexibility of the force simulation is centered around D3 force functions which adjust the position and velocity of elements to achieve a number of effects, such as attraction, repulsion and collision detection.
    We used the forceCollide() function to prevent elements overlapping and forceX()/forceY() to cause elements to be attracted towards specified position(s).
    To get a perfect result, we match values of forceX(), forceY() and forceCollide(). The number of iterations and strength parameters are also important in getting things to look how we want.
    We also used a custom force to constrain the position of nodes within the rectangular bounds of the containing SVG element.
    The nodes aren’t able to go outside these bounds; we update the node positions to be within the range [radius, width - radius] for x, [radius, height - radius] for y.

    Responsiveness

    The chart is very flexible. It responds to any changes. The js code recalculates the width and height of the chart, operator, tooltips’ position, ticks and ranges.
    The chart is recalibrated on:
    • resize
    • tab click
    • filter click (‘all’ or selected operators)
    In summary, the D3 library is a very powerful tool that allows you to apply data-driven transformations to the document. We hope you try it!

    Written by markupus | Front-end developer at Markupus
    Published by HackerNoon on 2020/03/01