Public
Edited
Mar 4
Importers
1 star
Insert cell
Insert cell
draw_tourney(
[
{ id: "1-4" },
{ id: "1-8", parentId: "1-4" },
{ id: "4-5", parentId: "1-4" },
{ id: "1-16a", parentId: "1-8" },
{ id: "8-9", parentId: "1-8" },
{ id: "4-13", parentId: "4-5" },
{ id: "5-12", parentId: "4-5" },
{ id: "16a-16b", parentId: "1-16a" }
],
{
games: new Map([
[
"16a-16b",
{
top_team: "UNCA",
bot_team: "TX Southern",
top_score: 92,
bot_score: 84
}
],
[
"1-16a",
{
top_team: "Texas",
bot_team: "UNCA",
top_score: 82,
bot_score: 61
}
],
[
"8-9",
{
top_team: "LSU",
bot_team: "Purdue",
top_score: 56,
bot_score: 80
}
],
[
"4-13",
{
top_team: "Stanford",
bot_team: "San Diego",
top_score: 77,
bot_score: 69
}
],
[
"5-12",
{
top_team: "U Conn",
bot_team: "BYU",
top_score: 58,
bot_score: 53
}
],
[
"1-8",
{
top_team: "Texas",
bot_team: "Purdue",
top_score: 77,
bot_score: 67
}
],
[
"4-5",
{
top_team: "Stanford",
bot_team: "U Conn",
top_score: 74,
bot_score: 85
}
],
[
"1-4",
{
top_team: "Texas",
bot_team: "U Conn",
top_score: 85,
bot_score: 76,
top_champion: true
}
]
])
}
)
Insert cell
sec_games = {
let sec_games = [
["1-8", { top_team: "Alabama" }],
["2-7", { top_team: "Texas A&M" }],
["3-6", { top_team: "Kentucky" }],
["4-5", { top_team: "Missouri" }],
["5-12", { top_team: "Tennessee" }],
["6-11", { top_team: "Vanderbilt" }]
];
if (!show_sec_results) {
sec_games = sec_games.concat([
["7-10", { top_team: "Auburn", bot_team: "Arkansas" }],
["8-9", { top_team: "Florida", bot_team: "Mississippi St" }],
["11-14", { top_team: "Georgia", bot_team: "LSU" }],
["12-13", { top_team: "South Carolina", bot_team: "Mississippi" }]
]);
} else {
sec_games = sec_games.concat([
[
"7-10",
{
top_team: "Auburn",
bot_team: "Arkansas",
top_score: 73,
bot_score: 76
}
],
[
"8-9",
{
top_team: "Florida",
bot_team: "Mississippi St",
top_score: 68,
bot_score: 69
}
],
[
"11-14",
{ top_team: "Georgia", bot_team: "LSU", top_score: 67, bot_score: 72 }
],
[
"12-13",
{
top_team: "South Carolina",
bot_team: "Mississippi",
top_score: 68,
bot_score: 69
}
],
[
"5-12",
{
top_team: "Tennesee",
bot_team: "Mississippi",
top_score: 70,
bot_score: 55
}
],
[
"6-11",
{
top_team: "Vanderbilt",
bot_team: "LSU",
top_score: 77,
bot_score: 68
}
],
[
"1-8",
{
top_team: "Alabama",
bot_team: "Mississippi St",
top_score: 72,
bot_score: 49
}
],
[
"4-5",
{
top_team: "Missouri",
bot_team: "Tennessee",
top_score: 79,
bot_score: 71
}
],
[
"2-7",
{
top_team: "Texas A&M",
bot_team: "Arkansas",
top_score: 67,
bot_score: 61
}
],
[
"3-6",
{
top_team: "Kentucky",
bot_team: "Vanderbilt",
top_score: 73,
bot_score: 80
}
],
[
"1-4",
{
top_team: "Alabama",
bot_team: "Missouri",
top_score: 72,
bot_score: 61
}
],
[
"2-3",
{
top_team: "Texas A&M",
bot_team: "Vanderbilt",
top_score: 87,
bot_score: 75
}
],
[
"1-2",
{
top_team: "Alabama",
bot_team: "Texas A&M",
top_score: 82,
bot_score: 63,
top_champion: true
}
]
]);
}

return new Map(sec_games);
}
Insert cell
Insert cell
draw_tourney(sec_structure, { games: sec_games })
Insert cell
sec_structure = [
{ id: "1-2" },
{ id: "1-4", parentId: "1-2" },
{ id: "2-3", parentId: "1-2" },
{ id: "1-8", parentId: "1-4" },
{ id: "4-5", parentId: "1-4" },
{ id: "2-7", parentId: "2-3" },
{ id: "3-6", parentId: "2-3" },
{ id: "6-11", parentId: "3-6" },
{ id: "7-10", parentId: "2-7" },
{ id: "11-14", parentId: "6-11" },
{ id: "8-9", parentId: "1-8" },
{ id: "5-12", parentId: "4-5" },
{ id: "12-13", parentId: "5-12" }
]
Insert cell
Insert cell
draw_tourney(make_powerTwo_links(3), {
sep: function (a, b) {
return a.parent == b.parent ? 10 : 12;
},
team_width: 150,
margin: 70,
w: 500,
h: 300
})
Insert cell
function draw_tourney(links, opts = {}) {
let {
games,
sep = function (a, b) {
return a.parent == b.parent ? 1 : 2;
},
w = 800,
h = 0.625 * w,
margin = 60,
game_height = 30,
team_width = 120
} = opts;

let div = d3
.create("div")
.style("width", `${w}px`)
.attr("height", `${h}px`)
.style("position", "relative");

let svg = div
.append("svg")
.attr("width", w)
.attr("height", h)
.style("overflow", "visible");
let g = svg.append("g");

let scale = d3
.scaleLinear()
.domain([0, w])
.range([w - margin, margin]);

let root = d3.hierarchy(d3.stratify()(links));
d3.tree().separation(sep).size([h, w])(root);

let link_display = g.append("g").attr("id", "links");
link_display
.selectAll("path")
.data(root.links())
.join("path")
.attr("d", function (o) {
let x1 = scale(o.source.y);
let y1 = o.source.x;
let x2 = scale(o.target.y);
let y2 = o.target.x;
let line = d3.line()([
[x1, y1],
[x1, y2],
[x2, y2]
]);
return line;
})
// .attr("class", get_linked_team)
.attr("fill", "none")
.attr("stroke", "#222")
.attr("stroke-width", 1.5);

let game_containers = div
.selectAll("div.game")
.data(root.descendants())
.enter()
.append(function (d) {
d.left = scale(d.y) - team_width / 2 + "px";
d.top = d.x - game_height / 2 + "px";
return game_container(d, {
// left: scale(d.y) - team_width / 2 + "px",
// top: d.x - game_height / 2 + "px",
game_height,
team_width,
game_data: games ? games.get(d.data.data.id) : null
});
});

return div.node();

function get_linked_team(link) {
let teams = [];
let source_links = link.source.data;
let source_data = games.get(link.source.data.data.id);
let target_data = games.get(link.target.data.data.id);

let link_team;
if (source_data && target_data) {
let source_teams = [];
if (source_data.top_team) {
source_teams.push(source_data.top_team);
}
if (source_data.bot_team) {
source_teams.push(source_data.bot_team);
}
let target_teams = [];
if (target_data.top_team) {
target_teams.push(target_data.top_team);
}
if (target_data.bot_team) {
target_teams.push(target_data.bot_team);
}
link_team = _.intersection(source_teams, target_teams);
}

if (link_team && link_team.length == 1) {
return link_team[0].replace(/ /g, "_");
}
}
}
Insert cell
Insert cell
function game_container(game_node, opts = {}) {
let { team_width, game_height, position, game_data } = opts;
let div = d3
.create("div")
.style("width", team_width + "px")
.style("height", game_height + "px")
.style("left", game_node.left)
.style("top", game_node.top)
.style("border", "solid 0.5px black")
.style("position", "absolute");

let game_id = game_node.data.id;
let top_team, bot_team, top_score, bot_score;
if (game_data) {
top_team = game_data.top_team;
bot_team = game_data.bot_team;
top_score = game_data.top_score;
bot_score = game_data.bot_score;
}

let top_data = {
team: top_team,
score: top_score,
team_width,
team_height: game_height / 2
};
if (game_data && game_data.top_champion) {
top_data.champion = true;
}
let bot_data = {
team: bot_team,
score: bot_score,
team_width,
team_height: game_height / 2
};
if (game_data && game_data.bot_champion) {
bot_data.champion = true;
}
if (game_id && !game_node.children) {
let seeds = game_id.split("-");
top_data.seed = seeds[0];
bot_data.seed = seeds[1];
} else if (game_id && game_node.children && game_node.children.length == 1) {
top_data.seed = game_id.split("-")[0];
}
div
.append(() => team_container("top", top_data))
div.append(() => team_container("bot", bot_data));

return div.node();
}
Insert cell
// Each game_container contains two team_containers
// They're displayed slightly differently depending on
// round and whether they're on the top or bottom (tb).
function team_container(tb, opts = {}) {
let { team, score, seed, champion, team_width = 40, team_height = 10 } = opts;
let div = d3
.create("div")
.attr("class", function (d) {
let label = "team " + tb + "_team";
if (team) {
label = label + " " + team.replace(/ /g, "_");
if (champion) {
label = label + " " + "champion";
}
}
return label;
})
.style("width", `${team_width}px`)
.style("height", `${team_height}px`)
.style("line-height", `${team_height}px`);
if (team) {
div.attr("data-team", team.replace(/ /g, "_"));
}

// Add name and score
if (seed) {
let seed_span = div
.append("span")
.attr("class", "seed")
.text(seed + " ");
}
if (team) {
div.append("span").text(team);
}
if (score) {
div
.append("span")
.attr("class", "score")
.style("height", `${team_height}px`)
.text(score);
}

return div.node();
}
Insert cell
make_links(10)
Insert cell
Insert cell
make_powerTwo_links(4).filter(function (link) {
let [s1, s2] = link.id.split("-").map((s) => parseInt(s));
return s1 < 12 && s2 < 12;
})
Insert cell
Insert cell
// Modified only slightly from
// https://observablehq.com/@d3/d3-hierarchy
function graph(
root,
{ label = (d) => d.data.id, highlight = () => false, marginLeft = 40 } = {}
) {
let dx = 12;
let dy = 120;
let tree = d3
.tree()
.separation(() => 3)
.nodeSize([dx, dy]);
let treeLink = d3
.linkHorizontal()
.x((d) => d.y)
.y((d) => d.x);
root = tree(root);

let x0 = Infinity;
let x1 = -x0;
root.each((d) => {
if (d.x > x1) x1 = d.x;
if (d.x < x0) x0 = d.x;
});

const svg = d3
.create("svg")
.attr("viewBox", [0, 0, width, x1 - x0 + dx * 2])
.style("overflow", "visible");

const g = svg
.append("g")
.attr("font-family", "sans-serif")
.attr("font-size", 10)
.attr("transform", `translate(${marginLeft},${dx - x0})`);

const link = g
.append("g")
.attr("fill", "none")
.attr("stroke", "#555")
.attr("stroke-opacity", 0.4)
.attr("stroke-width", 1.5)
.selectAll("path")
.data(root.links())
.join("path")
.attr("stroke", (d) =>
highlight(d.source) && highlight(d.target) ? "red" : null
)
.attr("stroke-opacity", (d) =>
highlight(d.source) && highlight(d.target) ? 1 : null
)
.attr("d", treeLink);

const node = g
.append("g")
.attr("stroke-linejoin", "round")
.attr("stroke-width", 3)
.selectAll("g")
.data(root.descendants())
.join("g")
.attr("transform", (d) => `translate(${d.y},${d.x})`);

node
.append("circle")
.attr("fill", (d) => (highlight(d) ? "red" : d.children ? "#555" : "#999"))
.attr("r", 2.5);

node
.append("text")
.attr("fill", (d) => (highlight(d) ? "red" : null))
.attr("stroke", "white")
.attr("paint-order", "stroke")
.attr("dy", "0.31em")
.attr("x", (d) => (d.children ? -6 : 6))
.attr("text-anchor", (d) => (d.children ? "end" : "start"))
.text(label);

return svg.node();
}
Insert cell
styles = html`
<style>
text {
cursor: default
}
.team {
cursor: default;
white-space: nowrap;
overflow: hidden;
font: 14px sans-serif;
}
.championship {
cursor: default;
white-space: nowrap;
overflow: hidden;
font: 16px sans-serif;
}
.score {
display: inline-block;
position: absolute;
right: 0px;
color: white;
background-color: #333333;
}
.top_team {
background-color: #efefef
}
.bot_team {
background-color: #dedede
}
.upset {
background-color: #dd9999
}
.champion {
background-color: #ffd700
}

</style>`
Insert cell
draw_tourney(make_links(9), { games: big_south_games2025 })
Insert cell
big_south_games2025 = new Map([
["1-8", { top_team: "High Point" }],
["2-7", { top_team: "UNC Asheville", bot_team: "Charleston So" }],
["3-6", { top_team: "Winthrop", bot_team: "Longwood" }],
["4-5", { top_team: "Radford", bot_team: "Presbyterian" }],
["8-9", { top_team: "Gardner Webb", bot_team: "SC Upstate" }]
])
Insert cell
sec_games
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more