diff --git a/src/index.md b/src/index.md
index 94dbf4c..f1a3a52 100644
--- a/src/index.md
+++ b/src/index.md
@@ -17,8 +17,9 @@ const coerceGameData = (d, i) => ({
const oddsfile = FileAttachment("./data/odds_data.json").json().then((D) => D.map(coerceGameData));
```
```js
-const teamNames = FileAttachment("./data/teams.json").json();
+const teamNames = FileAttachment("./data/teams.json").json().then((d) => d.sort((a,b)=>a.label.localeCompare(b.label)))
```
+
```js
function parseWeek(w)
{
@@ -32,35 +33,55 @@ function parseWeek(w)
```
```js
-const teamSelect = view(Inputs.select(teamNames, {
+teamNames.unshift({label:"All",alts:["All"]});
+
+const teamSelect = Inputs.select(teamNames, {
format: (d) => d.label,
valueof: (d) => d.alts,
+ value: teamNames[0].alts,
label: "Favorite"
-}))
+})
+const teamValue = Generators.input(teamSelect);
```
```js
-const highlight = view(Inputs.radio([{"label":"Fav vs. Und", "value":0}, {"label":"Predicted Score","value":1}], {value: 0, format: (x) => x.label}));
+const highlight = view(
+ Inputs.radio(
+ new Map([
+ ["Favorite vs. Underdog", 0],
+ ["Predicted Score",1]
+ ]),
+ {
+ value: 0,
+ }
+ )
+);
```
```js
function oddsPlot(d, {width} = {}) {
- const data = d.filter(function (game) {
- const whitelist = teamSelect;
- return whitelist.indexOf(game.fav) > -1 || whitelist.indexOf(game.und) > -1;
- });
+ let data = {};
+ if(teamValue[0] != "All")
+ {
+ data = d.filter(function (game) {
+ const whitelist = teamValue;
+ return whitelist.indexOf(game.fav) > -1 || whitelist.indexOf(game.und) > -1;
+ });
+ }else{
+ data = d;
+ }
const marginTop = 20;
const marginRight = 20;
const marginBottom = 20;
const marginLeft = 30;
const yearGroups = d3.group(data, (d) => d.year);
- const yearN = Array.from(yearGroups).length;
+ const yearN = d3.max(data, (d) => d.year)-1952;
const gameCount = longestString(yearGroups)[1].length;
const padding = 1;
const blockWidth =
- (width - marginLeft - marginRight) / Array.from(yearGroups).length -
+ (width - marginLeft - marginRight) / yearN -
padding;
- const blockHeight = d3.max([4, 500/gameCount]);
+ const blockHeight = blockWidth;
const height =
gameCount * (blockHeight + padding) +
marginBottom +
@@ -69,7 +90,7 @@ function oddsPlot(d, {width} = {}) {
const x = d3
.scaleLinear()
- .domain(d3.extent(data, (d) => d.year))
+ .domain([1952,d3.max(data, (d) => d.year)])
.range([marginLeft, width - marginRight]);
let yearCount = {};
@@ -82,19 +103,13 @@ function oddsPlot(d, {width} = {}) {
return marginTop + yearCount[d.year] * (blockHeight + padding);
};
- const playoffCount = gameCount - d3.count(longestString(yearGroups)[1], (d) => d.week);
-
- const yAxis = d3
- .scaleLinear()
- .domain(d3.extent(data, (d) => d.week))
- .range([marginTop+blockHeight, (height - marginBottom-blockHeight)-(blockHeight*playoffCount)]);
const svg = d3
.create("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [0, 0, width, height])
- .attr("style", "max-width: 100%; height: auto; background-color: grey;");
+ .attr("style", "max-width: 100%; height: auto; background-color: black;");
@@ -117,25 +132,59 @@ function oddsPlot(d, {width} = {}) {
.ticks(yearInterval, "^c")
);
- svg
- .append("g")
- .attr("transform", `translate(${marginLeft-blockHeight/2},0)`)
- .call(d3.axisLeft(yAxis).ticks(gameCount-playoffCount, "^c"))
-
svg
.append("g")
.selectAll()
.data(data)
.join("g")
+ .attr("class","gameBlock")
.attr(
"transform",
(d) => `translate(${x(d.year) - blockWidth / 2},${y(d)})`
)
- .html((d) => buildDualSquare(d, blockWidth, blockHeight));
+ .html((d) => buildDualSquare(d, blockWidth, blockHeight))
+ .on("mouseover", function(event, d){
+ infoTextContainer.html("game!")
+ const destX = x(d.year)-blockWidth/2;
+ const destY = y(d);
+ showTooltip(destX, destY);
+ })
+ .on("mouseout", function(event, d){
+ hideToolTip();
+ });
+
+
+ const infoBox = svg.append("g")
+ .attr("class", "info-box")
+ .attr("x", 5)
+ .attr("y", 5)
+ .attr("transform", `translate(-1000,-1000)`)
+
+
+ const infoRect = infoBox.append("rect");
+
+ const infoTextContainer = infoBox.append("g")
+ .attr("fill", "white")
+ .attr("dy", 20)
+ .attr("width", 120)
+
+
+ function showTooltip(x,y)
+ {
+ infoBox.attr("transform", `translate(${x}, ${y})`);
+ infoBox.transition().delay(100).duration(500).ease(d3.easeBackOut).attr("opacity",1)
+ }
+ function hideToolTip()
+ {
+ infoBox.transition().duration(500).ease(d3.easeBackOut).attr("opacity",0)
+ .transition().delay(500).attr("transform", "translate(-1000,-1000)")
+ }
+
return svg.node();
}
+
function longestString(map) {
return Array.from(map).sort(function (a, b) {
return b[1].length - a[1].length;
@@ -145,79 +194,90 @@ function longestString(map) {
function buildDualSquare(d, width, height) {
const blockWidth = width;
const blockHeight = height;
+ const strokeWidth = 2;
- let leftColor = "#EEE";
- let rightColor = "#EEE";
+ let blockColor = "#EEE";
+ let fullStroke = "none";
+ let winFill = "none";
+ let beatSpreadFill = "none";
- if(highlight.value==0)
+ if(highlight==0)
{
- if(teamSelect.indexOf(d.fav) > -1)
- {
- leftColor = "green";
- }
- if(teamSelect.indexOf(d.und) > -1)
- {
- rightColor = "orange";
- }
+ if(d.sF > d.sU)
+ {
+ winFill = "rgba(255,255,255,0.75)"
+ }
+ if(d.sF - d.sU > d.spread)
+ {
+ beatSpreadFill = "rgb(0,0,0,0.75)"
+ }
+ if(Math.abs(d.sF - d.sU) < d.spread && d.sF < d.sU)
+ {
+ beatSpreadFill = "rgb(0,0,0,0.75)"
+ }
+ if(teamValue[0]=="All")
+ {
+ blockColor = "#F25781";
+ }else{
+ if(teamValue.indexOf(d.fav) > -1)
+ {
+ blockColor = "#F25781";
+ }
+ if(teamValue.indexOf(d.und) > -1)
+ {
+ blockColor = "#0277D1";
+ if(d.sU > d.sF)
+ {
+ winFill = "rgba(255,255,255,0.75)"
+ }
+ }
+ if(d.week == undefined)
+ {
+ fullStroke = "rgba(255,255,255,0.5)";
+ }
+ }
+
}else{
if (d.pScore.sF) {
- const aboveLightness = d3
- .scaleLinear()
- .domain([
- 0,
- d3.max([
- d3.max(oddsfile, (d) => d.pScore.sF - d.sF),
- d3.max(oddsfile, (d) => d.pScore.sU - d.sU)
- ])
- ])
- .range([50, 90]);
-
- const belowLightness = d3
- .scaleLinear()
- .domain([
- d3.min([
- d3.min(oddsfile, (d) => d.pScore.sF - d.sF),
- d3.min(oddsfile, (d) => d.pScore.sU - d.sU)
- ]),
- 0
- ])
- .range([90, 50]);
-
- if (d.pScore.sF == d.sF) {
- leftColor = "red";
- } else if (d.pScore.sF > d.sF) {
- leftColor = `hsl(43,100%,${belowLightness(d.pScore.sF - d.sF)}%)`;
- } else {
- leftColor = `hsl(167,100%,${aboveLightness(d.pScore.sF - d.sF)}%)`;
- }
-
- if (d.pScore.sU == d.sU) {
- rightColor = "red";
- } else if (d.pScore.sU > d.sU) {
- rightColor = `hsl(43,100%,${belowLightness(d.pScore.sU - d.sU)}%)`;
- } else {
- rightColor = `hsl(167,100%,${aboveLightness(d.pScore.sU - d.sU)}%)`;
- }
+ const combinedScore = d.sF + d.sU;
+ const combinedPredictedScore = d.pScore.sF + d.pScore.sU;
+ const pDiff = Math.abs((combinedScore - combinedPredictedScore)/combinedScore);
+ blockColor = d3.interpolateOrRd(pDiff);
} else {
- leftColor = "#EEE";
- rightColor = "#EEE";
+ blockColor = "#555";
}
}
-
-
return `