chart = {
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height]);
let y = 'SHOT_against';
let x = 'SHOT_for';
let keep = [y, x];
let sliced = contrast.map(row => ['team', ...keep].reduce((acc, v) => ({ ...acc, [v]: row[v] }), {}));
const dots = svg.append("g");
let xScale = d3.scaleLinear()
.domain(d3.extent(sliced, d => Number(d[x]))).nice()
.range([margin.left, width - margin.right]);
let yScale = d3.scaleLinear()
.domain(d3.extent(sliced, d => Number(d[y]))).nice()
.range([height - margin.bottom, 0]);
let mainxAxis = svg.append("g").attr("transform", `translate(0,${height - margin.bottom})`);
let mainyAxis = svg.append("g").attr("transform", `translate(${margin.left},0)`);
let meandots = mainyAxis.append('g');
const meanAxis = object => object
.attr("transform", `translate(0,${yScale(25)})`)
.call(d3.axisRight(yScale)
.ticks(2)
.tickSize(width - margin.left - margin.right))
.style('stroke', 'red')
.call(g => g.select(".domain")
.remove())
.call(g => g.selectAll(".tick:not(:first-of-type) line")
.attr("stroke-opacity", 1.0)
.attr("stroke-dasharray", "6"))
.call(g => g.selectAll(".tick text")
.text("the mean line")
.attr("x", 4)
.attr("dy", -4));
let yAxis = d3
.axisLeft(yScale)
.tickFormat(formatTicks)
let xAxis = d3
.axisBottom(xScale)
.tickFormat(formatTicks)
function sety()
{
y = 'PASS_for';
keep = [y, x];
sliced = contrast.map(row => ['team', ...keep].reduce((acc, v) => ({ ...acc, [v]: row[v] }), {}));
console.log(sliced);
update(sliced, y, x);
}
d3.select(shot).on('click', sety);
d3.select(pass).on('click', sety);
const padding = 20;
function update(data, y, x)
{
yScale = d3.scaleLinear()
.domain(d3.extent(data, d => Number(d[y]))).nice()
.range([height - margin.bottom, 0])
xScale = d3.scaleLinear()
.domain(d3.extent(data, d => Number(d[x]))).nice()
.range([margin.left, width - margin.right])
yAxis = d3
.axisLeft(yScale)
.tickFormat(formatTicks)
xAxis = d3
.axisBottom(xScale)
.tickFormat(formatTicks)
const colorScale = d3.scaleSequential(d3.interpolateYlOrRd)
// .range(d3.interpolateViridis)
.domain([d3.min(data.map(d => Number(d[x])*Number(d[y]))),d3.max(data.map(d => Number(d[x])*Number(d[y])))]);
function addLabel(axis, label, xposition)
{
axis
.selectAll('.tick:last-of-type text')
.clone()
.text(label)
.attr('x', xposition)
.style('text-anchor', 'start')
.style('font-weight', 'semi-bold')
.style('fill', 'black')
}
mainxAxis
.call(xAxis)
.call(addLabel, x, 25);
mainyAxis
.call(yAxis)
.call(addLabel, y, 25);;
meandots.call(meanAxis);
dots
// .call(meanAxis)
.selectAll(".scatter")
.data(data, d=> d.team)
// .join("circle")
// .attr('class', 'scatter')
// .attr('hello', d => console.log(d[x]))
// .attr("cx", d => xScale(Number(d[x])))
// .attr("cy", d => yScale(Number(d[y])))
// .attr("r", 5)
// .attr("id", (d, i) => `${i}`)
// .style('stroke', 'dodgerblue')
// .style("fill", (d) => colorScale((Number(d[x])*Number(d[y]))))
// .style("fill-opacity", 0.25)
// .on("mouseenter", showData)
// .on('mouseleave', (event, d) => {
// dots.selectAll('.scatter').style('opacity', 1.0);
// d3.selectAll('.labelGroup').remove();
// });
.join(
(enter) => enter
.append('circle')
.attr('class', 'scatter')
.attr("cx", d => xScale(Number(d[x])))
.attr("cy", d => yScale(Number(d[y])))
.attr("r", 5)
.attr("id", (d, i) => `${i}`)
.style('stroke', 'dodgerblue')
.style("fill", (d) => colorScale((Number(d[x])*Number(d[y]))))
.style("fill-opacity", 0.25),
(update) => update
.attr("cx", d => xScale(Number(d[x])))
.attr("cy", d => yScale(Number(d[y]))),
(exit) => exit.remove())
.on("mouseenter", showData)
.on('mouseleave', (event, d) => {
dots.selectAll('.scatter').style('opacity', 1.0);
d3.selectAll('.labelGroup').remove();
});
function showData(event, d)
{
const current = d3.select(this);
const currentTeam = d.team;
console.log("team", currentTeam)
dots
.selectAll('.scatter')
.style("opacity", function(t) {
if (t.team !== currentTeam)
{
return 0.2;
}
})
let xpos = parseFloat(current.attr('cx'));
let ypos = parseFloat(current.attr('cy'));
console.log("Xpositions {}", xpos);
//const labelgroup = current.join('g').attr('class', 'labelGroup');
const labelgroup = current.selectAll('.labelGroup').join('g').attr('class', 'labelGroup');
//also tried to append this to labelgroup but I am not sure about the relative location
// const label = svg
const label = svg
.append('text')
.attr('class', 'bar-label text-sm labelGroup labelText')
.attr('fill', 'black')
// .attr('x', 0)
// .attr('y', 0)
.attr('x', xpos + padding)
.attr('y', ypos + padding);
const team = label
.append('tspan')
.style('text-anchor', 'start')
.style('font-weight', 'bold')
.style('font-family', 'Arial')
.text(`Team: ${d.team}`);
const textheight = team.node().getBBox().height;
// const textheight = 10;
label
.append('tspan')
.attr('class', 'bar-label text-sm')
.attr('fill', 'black')
.style('text-anchor', 'start')
.style('font-weight', 'bold')
.style('font-family', 'Arial')
.attr('x', xpos + padding)
.attr('dy', textheight)
.text(`Shots conceded: ${Number(d.against)}`);
label
.append('tspan')
.attr('class', 'bar-label text-sm')
.attr('fill', 'black')
.style('text-anchor', 'start')
.style('font-weight', 'bold')
.style('font-family', 'Arial')
.attr('x', xpos + padding)
.attr('dy', textheight)
.text(`Shots attempted: ${Number(d[x])}`)
const labelBBox = label.node().getBBox();
const tooltipWidth = labelBBox.width + padding * 2;
const tooltipHeight = labelBBox.height + padding;
console.log(labelBBox);
console.log(tooltipHeight);
//const tooltipWidth = 200;
//const tooltipHeight = 200;
const tooltip = svg
//.append('rect')
.insert('rect', '.labelText')// Why does this not work?
.attr('id', 'tooltip')
.attr('class', 'labelGroup')
.attr('y', ypos)
.attr('x', xpos)
.attr('rx', 5)
.attr('ry', 5)
// .attr('x', (xpos + tooltipWidth > width) ? xpos - tooltipWidth : xpos)
// .attr('y', (ypos + tooltipHeight > height) ? ypos - tooltipHeight : ypos)
.style('fill', 'black')
.style('opacity', 0.5)
.attr('width', tooltipWidth)
.attr('height', tooltipHeight);
}
}
update(sliced, y, x);
return svg.node();
}