Added legend, cleaned up selections, cleaned up colors, changed colors and symbols for All-FvsU

This commit is contained in:
Gabi 2025-02-06 13:32:04 -05:00
parent d3d6d5c7d9
commit 33f10ee40b

@ -1,3 +1,7 @@
```js
import("https://kit.fontawesome.com/e791529cc8.js");
```
```js ```js
const parseTime = d3.timeParse("%b %d, %Y"); const parseTime = d3.timeParse("%b %d, %Y");
const formatYear = d3.utcFormat("%Y"); const formatYear = d3.utcFormat("%Y");
@ -7,6 +11,7 @@ const coerceGameData = (d, i) => ({
year: parseInt(d.week.slice(0, 4)), year: parseInt(d.week.slice(0, 4)),
spread: Number(d.spread), spread: Number(d.spread),
fav: d.fav.replace(/\s\(\d\)/, ""), fav: d.fav.replace(/\s\(\d\)/, ""),
at: d.at,
und: d.und.replace(/\s\(\d\)/, ""), und: d.und.replace(/\s\(\d\)/, ""),
sF: Number(d.sF), sF: Number(d.sF),
sU: Number(d.sU), sU: Number(d.sU),
@ -20,7 +25,17 @@ const oddsfile = FileAttachment("./data/odds_data.json").json().then((D) => D.ma
```js ```js
const teamNames = FileAttachment("./data/teams.json").json().then((d) => d.sort((a,b)=>a.label.localeCompare(b.label))) const teamNames = FileAttachment("./data/teams.json").json().then((d) => d.sort((a,b)=>a.label.localeCompare(b.label)))
``` ```
```js
const defaultBlockFill = "#EEE";
const noOUFill = "#555";
const winCircleFill = "rgba(255,255,255,0.75)";
const coverCircleFill = "rgb(0,0,0,0.75)";
const favFill = "#F25781";
const undFill = "#0277D1";
const neutFill = "#5C7553";
const neutFillSecondary = "#8F8A40"
const playOffStroke = "rgba(255,255,255,0.5)";
```
```js ```js
function parseWeek(w) function parseWeek(w)
{ {
@ -52,7 +67,7 @@ teamNames.unshift({label:"All",alts:["All"]});
const teamSelect = Inputs.select(teamNames, { const teamSelect = Inputs.select(teamNames, {
format: (d) => d.label, format: (d) => d.label,
valueof: (d) => d.alts, valueof: (d) => d.alts,
value: teamNames[0].alts, value: teamNames[26].alts,
label: "Team:" label: "Team:"
}) })
const teamValue = Generators.input(teamSelect); const teamValue = Generators.input(teamSelect);
@ -65,6 +80,14 @@ const viewSelect = Inputs.radio(
]), ]),
{ {
value: 0, value: 0,
format:(x)=>{
if(x[1]==1)
{
return html`Predicted Score <i class="fa-regular fa-circle-question"></i>`
}else{
return "Favorite vs. Underdog"
}
}
} }
); );
const viewValue = Generators.input(viewSelect); const viewValue = Generators.input(viewSelect);
@ -220,55 +243,78 @@ function buildDualSquare(d, width, height) {
const blockHeight = height; const blockHeight = height;
const strokeWidth = 2; const strokeWidth = 2;
let blockColor = "#EEE"; let blockColor = defaultBlockFill;
let fullStroke = "none"; let fullStroke = "none";
let winFill = "none"; let winFill = "none";
let beatSpreadFill = "none"; let beatSpreadFill = "none";
let isAll = (teamValue[0=="All"]);
if(viewValue==0) if(viewValue==0)
{ {
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") if(teamValue[0]=="All")
{ {
blockColor = "#545C32"; if(d.sF > d.sU && d.sF - d.sU == d.spread)//fav doesn't cover, wins
{
blockColor = neutFill;
}
if(d.sF > d.sU && d.sF - d.sU < d.spread)//fav doesn't cover, wins
{
blockColor = neutFill;
winFill = winCircleFill;
}
if(d.sF - d.sU > d.spread)//favorite covers, wins
{
blockColor = neutFill;
winFill = winCircleFill;
beatSpreadFill = coverCircleFill;
}
if(d.sF <= d.sU)//underdog covers, wins
{
blockColor = neutFillSecondary;
}
}else{ }else{
if(d.sF > d.sU)//underdog covers, loses
{
winFill = winCircleFill;
}
if(d.sF - d.sU > d.spread)//favorite covers, wins
{
beatSpreadFill = coverCircleFill;
}
if(Math.abs(d.sF - d.sU) < d.spread && d.sF < d.sU)//underdog covers, wins
{
beatSpreadFill = coverCircleFill;
}
if(teamValue.indexOf(d.fav) > -1) if(teamValue.indexOf(d.fav) > -1)
{ {
blockColor = "#F25781"; blockColor = favFill;
} }
if(teamValue.indexOf(d.und) > -1) if(teamValue.indexOf(d.und) > -1)
{ {
blockColor = "#0277D1"; blockColor = undFill;
if(d.sU > d.sF) if(d.sU > d.sF)
{ {
winFill = "rgba(255,255,255,0.75)" winFill = winCircleFill;
} }
} }
if(d.week == undefined)
{
fullStroke = "rgba(255,255,255,0.5)";
}
} }
}else{ }else{
if (d.pDiff) { if (d.pDiff) {
blockColor = d3.interpolateOrRd(diffColorScale(d.pDiff)); blockColor = d3.interpolateOrRd(diffColorScale(d.pDiff));
} else { } else {
blockColor = "#555"; blockColor = noOUFill;
} }
} }
if(d.week == undefined)
{
fullStroke = playOffStroke;
}
return ` return `
<rect x="0" y="0" height="${height}" width="${ <rect x="0" y="0" height="${height}" width="${
width width
@ -304,11 +350,105 @@ function buildToolTip(d)
return toolTipHTML; return toolTipHTML;
} }
``` ```
```js
function legend()
{
const legendNode = d3.create("div");
const isAll = (teamValue[0]=="All");
console.log(isAll);
if(!viewValue)
{
legendNode.html(
`
<table id="legend-table">
<tbody>
<tr>
<td></td>
<td>${isAll ? "push" : ""}</td>
<td>win</td>
<td>cover</td>
<td>playoffs</td>
</tr>
<tr>
<td>Favorite</td>
<td>${isAll ? `${legendSwatch(neutFill)}` : `${legendSwatch(favFill)}`}</td>
<td>${isAll ? `${legendSwatch(neutFill,false,true)}` : `${legendSwatch(favFill,false,true)}`}</td>
<td>${isAll ? `${legendSwatch(neutFill,true,true)}` : `${legendSwatch(favFill,true, true)}`}</td>
<td>${isAll ? `${legendSwatch(neutFill,false,false,true)}` : `${legendSwatch(favFill,false,false,true)}`}</td>
</tr>
<tr>
<td>Underdog</td>
<td>${isAll ? `` : `${legendSwatch(undFill)}`}</td>
<td>${isAll ? `${legendSwatch(neutFillSecondary)}` : `${legendSwatch(undFill,true,true)}`}</td>
<td>${isAll ? `${legendSwatch(neutFillSecondary)}` : `${legendSwatch(undFill,true, true)}`}</td>
<td>${isAll ? `${legendSwatch(neutFillSecondary,false,false,true)}` : `${legendSwatch(undFill,false,false,true)}`}</td>
</tr>
</tbody>
</table>
`
)
}else{
legendNode.html(
`
<svg width="100%" height="20" version="1.1" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="Gradient2" x1="0" x2="1" y1="0" y2="0">
<stop offset="0%" stop-color="${d3.interpolateOrRd(0)}"/>
<stop offset="100%" stop-color="${d3.interpolateOrRd(1)}"/>
</linearGradient>
</defs>
<rect x="0" y="0" width="100%" height="20" fill="url(#Gradient2)"/>
</svg>
<table id="legend-table">
<tbody>
<tr>
<td>Worst predictions</td>
<td style="text-align:right">Best predictions</td>
</tr>
<tr>
<td style="padding-top:5px">
${legendSwatch(noOUFill)} No O/U data
</td>
<td style="padding-top:5px">
${legendSwatch(d3.interpolateOrRd(1),false,false,true)} Playoffs
</td>
</tbody>
</table>
`
)
}
return legendNode.node();
}
function legendSwatch(bgFill,cover,win,playoffs)
{
const blockSize = 15;
const strokeWidth = 2;
return `
<svg viewBox="0 0 ${blockSize} ${blockSize}" width="${blockSize}" height="${blockSize}" xmlns="http://www.w3.org/2000/svg">
<rect x="0" y="0" width="${blockSize}" height="${blockSize}" fill="${bgFill}"/>
<circle cx="${blockSize/2}" cy="${blockSize/2}" r="${blockSize/4}" fill="${win ? winCircleFill : 'none'}"/>
<circle cx="${blockSize/2}" cy="${blockSize/2}" r="${blockSize/6}" fill="${cover ? coverCircleFill : 'none'}"/>
<rect x="${strokeWidth/2}" y="${strokeWidth/2}" height="${blockSize-strokeWidth}" width="${blockSize-strokeWidth}" stroke="${playoffs ? playOffStroke : "none"}" fill="none" stroke-width="${strokeWidth}"/>
</svg>
`
}
```
<div class="grid grid-cols-2"> <div class="grid grid-cols-2">
<div class="card">${teamSelect}${viewSelect}</div> <div class="card"><div>${teamSelect}</div><div style="padding-top:12px;">${viewSelect}</div></div>
<div class="card">Legend</div> <div class="card">${legend()}</div>
</div> </div>
<div class="grid grid-cols-1"> <div class="grid grid-cols-1">
<div class="card">${resize((width) => oddsPlot(oddsfile, {width}))}</div> <div class="card">${resize((width) => oddsPlot(oddsfile, {width}))}</div>
@ -378,6 +518,24 @@ function buildToolTip(d)
border: 2px white; border: 2px white;
} }
#legend-table
{
margin-top: 0px;
}
#legend-table td
{
width:50px;
}
#legend-table td:nth-child(1)
{
width: 50px;
padding-right: 8px;
}
.lexend-400 { .lexend-400 {
font-family: "Lexend", serif; font-family: "Lexend", serif;
font-optical-sizing: auto; font-optical-sizing: auto;