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 ` - + width + }" fill="${blockColor}" /> + + + `; } ``` +```js +function buildToolTip() +{ +} +``` + + + +
+
${teamSelect}
+
${resize((width) => oddsPlot(oddsfile, {width}))}
\ No newline at end of file