vis6.js

Asks JavaScript to show more errors.

"use strict";


Boilerplate jQuery

This code loads the file res/scores.json and calls the visualize function as soon as the JSON file is loaded.

$(function() {
  var fileName = "res/scores.json";
  $.getJSON(fileName)
   .done(function (data) {
     visualize(data);
   })
   .fail(function() {
     alert("Failed to load the JSON file: " + fileName);
   });
});


d3.js visualization

All of the code to create our visualization will be contained in the visualize function, which is called once the data for the visualization has been loaded by the boilerplate jQuery code.

var visualize = function(data) {
  console.log(data);


Data Pre-processing

The input data, in res/scores.json, is an array of dictionaries, where each dictionary is a single game played by the Illini. To bin the data by the opponents played, we need to pluck the opponent out of the dictionary and create an array of only the name of the opponents.

  var teams = data.map(function (d) {
    return d["Opponent"];
  });


After the pluck operation, teams is an array containing the name of the opponent for each game played. Since the Illini played many teams many times, we need to remove duplicates from this list.

  teams = d3.set(teams).values();
  console.log(teams);



The same logic is applied to get the set of unique years in the years variable.

  var years = data.map(function (d) {
    return d["Year"];
  });
  years = d3.set(years).values();
  console.log(years);



Boilerplate Code for d3.js

See: CS 205 Guidebook: Boilerplate Code in d3.js

  var margin = { top: 50, right: 50, bottom: 50, left: 150 },
     width = 800 - margin.left - margin.right,
     height = (teams.length * 20);

  var svg = d3.select("#chart6")
              .append("svg")
              .attr("width", width + margin.left + margin.right)
              .attr("height", height + margin.top + margin.bottom)
              .style("width", width + margin.left + margin.right)
              .style("height", height + margin.top + margin.bottom)
              .append("g")
              .attr("transform", "translate(" + margin.left + "," + margin.top + ")");




Opponent Scale (teamsScale)

The teams variable is an array of all of the opponents, where the opponent will be mapped to the y-position. As categorical, binned data, we use an ordinal scale.

  var teamsScale = d3.scale.ordinal()
                     .domain( teams )
                     .rangePoints( [0, height] );



Years Scale (yearsScale)

The years variable is an array of all years that we have data. Since each year is an individual season, this data is also binned data (not linear) and we use another ordinal scale.

  var yearsScale = d3.scale.ordinal()
                     .domain( years )
                     .rangePoints( [0, width] );





Axis for Opponents

Standard vertical axis, oriented left.

  var teamsAxis = d3.svg.axis()
                    .scale(teamsScale)
                    .orient("left");

  svg.append("g")
     .attr("class", "axis")
     .call(teamsAxis);


Axis for Years

Near standard horizontal axis, oriented top. Instead of allowing d3.js to draw every tick, we specify the tickValues as an array of values that we want d3.js to draw on the axis.

  var yearsAxis = d3.svg.axis()
                    .scale(yearsScale)
                    .tickValues( [2014, 2000, 1980, 1960, 1940, 1920, 1900, 1892] )
                    .orient("top");

  svg.append("g")
     .attr("class", "axis")
     .call(yearsAxis);



Helper functions



d3.tip

To allow for mouseover pop-up boxes, we use the d3.tip.js library for d3. More info: d3.tip on Github

  var tip = d3.tip()
              .attr('class', 'd3-tip')

Returns a string of the format: Illinois vs. Iowa (1952)
34-12

              .html(function(d) {
                return "Illinois " + d["Location"] + " " + d["Opponent"] + " (" + d["Year"] + ")<br>" +
                       d["IlliniScore"] + "-" + d["OpponentScore"];
              });
  svg.call(tip);



Function: didIllinoisWin

Given an opponent and a year, returns true if the Illini won. Otherwise, returns false. Used to assist in calculating the radius.

  var didIllinoisWin = function(opponent, year) {
    for (var i = 0; i < data.length; i++) {
      var d = data[i];

      if (d["Year"] == year && d["Opponent"] == opponent) {
        if (d["Result"] == "W") {
          return true;
        } else {
          return false;
        }
      }
    }

    return false;
  };



  svg.selectAll("circles")
     .data(data)
     .enter()


SVG Shape Appending a circle for each game played by the Illini football team

     .append("circle")


CSS Class

Adds the CSS class football-team to every circle. Used for selection in our mouseover handler.

     .attr("class", "football-team")


x-position (center)

Based on the year in the data, passed through the yearsScale scale

     .attr("cx", function (d, i) {
       return yearsScale( d["Year"] );
     })


y-position (center)

Based on the opponent in the data, passed through the teamsScale scale

     .attr("cy", function (d, i) {
       return teamsScale( d["Opponent"] );
     })


Radius

By default, the radius of every circle will be 2px. If the Illini are on a winning streak verses a given opponent, increase the size of the circle by 1px for each consecutive victory.

     .attr("r", function (d, i) {

Initial Values

       var radius = 2;
       var year = d["Year"];
       var opponent = d["Opponent"];


Start with the year before last

       year = year - 1;


Loop while the Illini are winning

       while ( didIllinoisWin(opponent, year) ) {
         year = year - 1;
         radius = radius + 1;
       }


Return the calculated radius as the radius

       return radius;
     })


Fill Color

  • Blue when the Illini won
  • Red when the Illini did not win (tie/loss/anything)
     .attr("fill", function (d, i) {
       if ( d["Result"] == "W" ) {
         return "hsla(255, 100%, 30%, 0.5)";
       } else {
         return "hsla(0, 100%, 30%, 0.5)";
       }
     })


On mouseover effect

On a mouseover, two things should happen:

  1. A tool-tip showing the teams, year, and score is displayed to the user (using d3.tip)
  2. All of the teams except those played against the opponent should be faded.
     .on('mouseover', function (d, i) {

Shows the d3.tip

       tip.show(d, i);


Select all of the elements with the class football-team that is found in the svg

       svg.selectAll(".football-team")

Filter each element such that we retain all elements where the opponent (e["Opponent"]) is not equal to the opponent that the user moused over (d["Opponent"]).

          .filter(function (e) {
            return (e["Opponent"] != d["Opponent"]);
          })

Apply a transition to the data, changing the opacity of those visual elements to 10% (0.1)

          .transition()
          .style("opacity", 0.1);
     })

On mouseout effect

Same as mouseover, but in reverse. This means:

  1. Hide the d3.tip.
  2. Set the opacity back to 100% (1.0)
     .on('mouseout', function (d, i) {
       tip.hide(d, i);

       svg.selectAll(".football-team")
          .filter(function (e) {
            return (e["Opponent"] != d["Opponent"]);
          })
          .transition()
          .style("opacity", 1);
     })
     ;
};