Monday 20 July 2020

Web Tutorial: D3 Pie Chart (Part 1/4)

After showing you how to make a bar and line chart in D3, the natural progression from such hallowed acts of achievement would be the pie chart. Sure, back in 2017, I walked you through creating a pie chart using only HTML, CSS and vanilla JavaScript. It was a lot of fun, but the math was just... ugh. There are far easier ways; namely various charting libraries.

And, of course, D3 is one such charting library.

The past two web tutorials used version 3 of D3, but for this one we will use version 4 because that's the version which actually has the functions we need to produce that pie. And unlike in the case of the line chart web tutorial a couple months back, we won't be reusing all much code because the operation is very different.

So let's get to it!

We begin with some boilerplate HTML. Once again, note that the D3 library we're using is version 4. Font size and type has been set.
<!DOCTYPE html>
<html>
    <head>
        <title>D3 Pie Chart</title>

        <style>
            body
            {
                font-size: 12px;
                font-family: arial;
            }
        </style>

        <script src="https://d3js.org/d3.v4.min.js"></script>
    </head>

    <body>
        <script>

        </script>
    </body>
</html>


In the body, insert a div with an class of pieChartContainer. That will contain all the elements of the entire setup. Then insert a div into that, and give it a class of pieDashboard.
<body>
    <div class="pieChartContainer">
        <div class="pieDashboard">

        </div>
    </div>
   
    <script>

    </script>
</body>


In that div, you'll want two drop-down lists. They're ddlYear and ddlStat.
<body>
    <div class="pieChartContainer">
        <div class="pieDashboard">
            <select id="ddlYear">

            </select>

            <select id="ddlStat">

            </select>
        </div>
    </div>
   
    <script>

    </script>
</body>


After this, insert another div into the div styled using pieChartContainer. This one has the pieChart class. It will hold the chart.
<body>
    <div class="pieChartContainer">
        <div class="pieDashboard">
            <select id="ddlYear">

            </select>

            <select id="ddlStat">

            </select>
        </div>

        <div class="pieChart">

        </div>
    </div>
   
    <script>

    </script>
</body>


Within this div, insert two svg tags. The first has a class of pieChartSvg, and the second pieLegendSvg. No prizes for guessing what they'll be doing, eh?
<body>
    <div class="pieChartContainer">
        <div class="pieDashboard">
            <select id="ddlYear">

            </select>

            <select id="ddlStat">

            </select>
        </div>

        <div class="pieChart">
            <svg class="pieChartSvg">

            </svg>

            <svg class="pieLegendSvg">

            </svg>
        </div>
    </div>
   
    <script>

    </script>
</body>


Let's do some styling. First, ensure that all divs have a red outline so you can see what you're doing.
<style>
    body
    {
        font-size: 12px;
        font-family: arial;
    }   

    div {outline: 1px solid #FF0000;}
</style>


Then add the class for pieChartContainer. We're going to specify width and height here using em, and set it middle of the page.
<style>
    body
    {
        font-size: 12px;
        font-family: arial;
    }   

    div {outline: 1px solid #FF0000;}
   
    .pieChartContainer
    {
        width: 100em;
        height: 20em;
        margin: 0 auto 0 auto;
    }
</style>


Next is pieDashboard. It has a small height and all of its parent's width. The text-align property will push the drop-down lists to the middle, since they're inline elements.
div {outline: 1px solid #FF0000;}

.pieDashboard
{
    height: 3em;
    width: 100%;
    text-align: center;
}

.pieChartContainer
{
    width: 100em;
    height: 20em;
    margin: 0 auto 0 auto;
}


And finally, pieChart. width and height are 100%.
div {outline: 1px solid #FF0000;}

.pieDashboard
{
    height: 3em;
    width: 100%;
    text-align: center;
}

.pieChart
{
    width: 100%;
    height: 100%;
}

.pieChartContainer
{
    width: 100em;
    height: 20em;
    margin: 0 auto 0 auto;
}


This is what you should see. You'll notice the div styled using pieChart has been pushed down because the dashboard occupies 3em height. No worries.


Now float pieChartSvg left, and pieLegendSvg right. Specify their widths using percentages (ensure that the combined total width doesn't exceed 100), and give both heights of 100%. Give them a random background color so we can see what's going on.
.pieChartContainer
{
    width: 100em;
    height: 20em;
    margin: 0 auto 0 auto;
}

.pieChartSvg
{
    width: 50%;
    height: 100%;
    float: left;
    background-color: rgba(200, 100, 100, 0.5);
}

.pieLegendSvg
{
    width: 40%;
    height: 100%;
    float: right;
    background-color: rgba(100, 200, 100, 0.5);
}


Here you'll see the shape of things.


Now for some data! We'll just reuse the graphData object from earlier web tutorials.
<script>
var graphData =
{
    "cols":
    [
        {
            "title": "Adam Lallana",
            "stats":
            [
                {"year": 2015, "goals": 7, "appearances": 49},
                {"year": 2016, "goals": 8, "appearances": 35},
                {"year": 2017, "goals": 0, "appearances": 15},
                {"year": 2018, "goals": 0, "appearances": 16}
            ]
        },
        {
            "title": "Sadio Mané",
            "stats":
            [
                {"year": 2016, "goals": 13, "appearances": 29},
                {"year": 2017, "goals": 20, "appearances": 44},
                {"year": 2018, "goals": 26, "appearances": 50}
            ]
        },
        {
            "title": "Roberto Firminho",
            "stats":
            [
                {"year": 2015, "goals": 11, "appearances": 49},
                {"year": 2016, "goals": 12, "appearances": 41},
                {"year": 2017, "goals": 27, "appearances": 54},
                {"year": 2018, "goals": 16, "appearances": 48}
            ]
        },
        {
            "title": "Divock Origi",
            "stats":
            [
                {"year": 2015, "goals": 10, "appearances": 33},
                {"year": 2016, "goals": 11, "appearances": 43},
                {"year": 2017, "goals": 0, "appearances": 1},
                {"year": 2018, "goals": 7, "appearances": 21}
            ]
        },
        {
            "title": "Daniel Sturridge",
            "stats":
            [
                {"year": 2015, "goals": 13, "appearances": 25},
                {"year": 2016, "goals": 7, "appearances": 27},
                {"year": 2017, "goals": 3, "appearances": 14},
                {"year": 2018, "goals": 4, "appearances": 27}
            ]
        },
        {
            "title": "James Milner",
            "stats":
            [
                {"year": 2015, "goals": 7, "appearances": 45},
                {"year": 2016, "goals": 7, "appearances": 40},
                {"year": 2017, "goals": 1, "appearances": 47},
                {"year": 2018, "goals": 7, "appearances": 45}
            ]
        },
    ],
    "rows": [2015, 2016, 2017, 2018],
    "stats": ["goals", "appearances"]
};
</script>


For every element in the cols array, let's add a property, color. Assign each player a different color.
<script>
var graphData =
{
    "cols":
    [
        {
            "title": "Adam Lallana",
            "color": "rgb(100, 50, 0)",
            "stats":
            [
                {"year": 2015, "goals": 7, "appearances": 49},
                {"year": 2016, "goals": 8, "appearances": 35},
                {"year": 2017, "goals": 0, "appearances": 15},
                {"year": 2018, "goals": 0, "appearances": 16}
            ]
        },
        {
            "title": "Sadio Mané",
            "color": "rgb(255, 0, 0)",
            "stats":
            [
                {"year": 2016, "goals": 13, "appearances": 29},
                {"year": 2017, "goals": 20, "appearances": 44},
                {"year": 2018, "goals": 26, "appearances": 50}
            ]
        },
        {
            "title": "Roberto Firminho",
            "color": "rgb(255, 50, 50)",
            "stats":
            [
                {"year": 2015, "goals": 11, "appearances": 49},
                {"year": 2016, "goals": 12, "appearances": 41},
                {"year": 2017, "goals": 27, "appearances": 54},
                {"year": 2018, "goals": 16, "appearances": 48}
            ]
        },
        {
            "title": "Divock Origi",
            "color": "rgb(255, 100, 0)",
            "stats":
            [
                {"year": 2015, "goals": 10, "appearances": 33},
                {"year": 2016, "goals": 11, "appearances": 43},
                {"year": 2017, "goals": 0, "appearances": 1},
                {"year": 2018, "goals": 7, "appearances": 21}
            ]
        },
        {
            "title": "Daniel Sturridge",
            "color": "rgb(0, 0, 0)",
            "stats":
            [
                {"year": 2015, "goals": 13, "appearances": 25},
                {"year": 2016, "goals": 7, "appearances": 27},
                {"year": 2017, "goals": 3, "appearances": 14},
                {"year": 2018, "goals": 4, "appearances": 27}
            ]
        },
        {
            "title": "James Milner",
            "color": "rgb(100, 0, 50)",
            "stats":
            [
                {"year": 2015, "goals": 7, "appearances": 45},
                {"year": 2016, "goals": 7, "appearances": 40},
                {"year": 2017, "goals": 1, "appearances": 47},
                {"year": 2018, "goals": 7, "appearances": 45}
            ]
        },
    ],
    "rows": [2015, 2016, 2017, 2018],
    "stats": ["goals", "appearances"]
};
</script>


While you're at it, add the config object. We won't use it just yet.
var graphData =
{
    "cols":
    [
        {
            "title": "Adam Lallana",
            "color": "rgb(100, 50, 0)",
            "stats":
            [
                {"year": 2015, "goals": 7, "appearances": 49},
                {"year": 2016, "goals": 8, "appearances": 35},
                {"year": 2017, "goals": 0, "appearances": 15},
                {"year": 2018, "goals": 0, "appearances": 16}
            ]
        },
        {
            "title": "Sadio Mané",
            "color": "rgb(255, 0, 0)",
            "stats":
            [
                {"year": 2016, "goals": 13, "appearances": 29},
                {"year": 2017, "goals": 20, "appearances": 44},
                {"year": 2018, "goals": 26, "appearances": 50}
            ]
        },
        {
            "title": "Roberto Firminho",
            "color": "rgb(255, 50, 50)",
            "stats":
            [
                {"year": 2015, "goals": 11, "appearances": 49},
                {"year": 2016, "goals": 12, "appearances": 41},
                {"year": 2017, "goals": 27, "appearances": 54},
                {"year": 2018, "goals": 16, "appearances": 48}
            ]
        },
        {
            "title": "Divock Origi",
            "color": "rgb(255, 100, 0)",
            "stats":
            [
                {"year": 2015, "goals": 10, "appearances": 33},
                {"year": 2016, "goals": 11, "appearances": 43},
                {"year": 2017, "goals": 0, "appearances": 1},
                {"year": 2018, "goals": 7, "appearances": 21}
            ]
        },
        {
            "title": "Daniel Sturridge",
            "color": "rgb(0, 0, 0)",
            "stats":
            [
                {"year": 2015, "goals": 13, "appearances": 25},
                {"year": 2016, "goals": 7, "appearances": 27},
                {"year": 2017, "goals": 3, "appearances": 14},
                {"year": 2018, "goals": 4, "appearances": 27}
            ]
        },
        {
            "title": "James Milner",
            "color": "rgb(100, 0, 50)",
            "stats":
            [
                {"year": 2015, "goals": 7, "appearances": 45},
                {"year": 2016, "goals": 7, "appearances": 40},
                {"year": 2017, "goals": 1, "appearances": 47},
                {"year": 2018, "goals": 7, "appearances": 45}
            ]
        },
    ],
    "rows": [2015, 2016, 2017, 2018],
    "stats": ["goals", "appearances"]
};

var config =
{
   
}


The next thing we will do, is populate the drop-down lists. If you've gone through any of the other web tutorials, I've probably repeated the instructions ad nauseam. It's basically the same as what we did for the bar chart.
var config =
{
   
}

var ddlYear = d3.select("#ddlYear");

ddlYear.selectAll("option")
.data(graphData.rows)
.enter()
.append("option")
.property("selected", function(d, i)
{
    return i == 0;
})
.attr("value", function(d)
{
    return d;
})
.text(function(d)
{
    return d;
});

var ddlStat = d3.select("#ddlStat");

ddlStat.selectAll("option")
.data(graphData.stats)
.enter()
.append("option")
.property("selected", function(d, i)
{
    return i == 0;
})
.attr("value", function(d)
{
    return d;
})
.text(function(d)
{
    return d;
});


So there you go - your drop-down lists!


Next

Things will get a bit complicated. We will dive right into creating your pie chart.

No comments:

Post a Comment