function brushScatter() {
const visWidth = scatter_sq;
const visHeight = 250;
const xCol = "li_pep_pct",
xLabel = "Low Income Price to Earning Ratio";
const yCol = "emi",
yLabel = "Economic Mobility Percentile Rank";
const initialValue = schoolData;
const svg = d3
.create("svg")
.attr("width", visWidth + margin_sc.left + margin_sc.right)
.attr("height", visHeight + margin_sc.top + margin_sc.bottom)
.property("value", initialValue);
const g = svg
.append("g")
.attr("transform", `translate(${margin_sc.left}, ${margin_sc.top})`);
const x = d3
.scaleLinear()
.domain([0, d3.max(schoolData, (d) => d[xCol])])
.nice()
.range([0, visWidth]);
const y = d3
.scaleLinear()
.domain([0, d3.max(schoolData, (d) => d[yCol])])
.nice()
.range([visHeight, 0]);
const xAxis = d3.axisBottom(x).tickFormat(format_pct);
const xAxisGroup = g
.append("g")
.attr("transform", `translate(0, ${visHeight})`);
//Grid Lines; https://observablehq.com/@d3/connected-scatterplot
xAxisGroup
.call(xAxis)
.call((g) =>
g
.selectAll(".tick line")
.clone()
.attr("stroke", "lightgrey")
.attr("y1", -visHeight)
.attr("y2", 0)
)
.append("text")
.attr("x", visWidth / 2)
.attr("y", 26)
.attr("fill", "black")
.attr("text-anchor", "middle")
.attr("font-weight", "bold")
.text(xLabel);
const yAxis = d3.axisLeft(y).tickFormat(format_pct);
const yAxisGroup = g.append("g");
yAxisGroup
.call(yAxis)
.call((g) =>
g
.selectAll(".tick line")
.clone()
.attr("stroke", "lightgrey")
.attr("x1", 0)
.attr("x2", visWidth)
)
.append("text")
.attr("transform", "rotate(-90)") //Flipping X and Y
.attr("x", -1 * (visHeight / 2))
.attr("y", -50)
.attr("fill", "black")
.attr("dominant-baseline", "middle")
.attr("font-weight", "bold")
.text("Economic Mobility Percentile");
//Adding Brush
const brush = d3
.brush()
//Size of brush area can't go outside plot
.extent([
[0, 0],
[visWidth, visHeight]
])
.on("brush", onBrush)
.on("end", onEnd);
g.append("g").call(brush);
//Drawing points
const radius = 3;
//National Data
const dots = svg
.append("g")
.attr("transform", `translate(${margin_sc.left}, ${margin_sc.top})`)
.selectAll("circle")
.data(schoolData)
.join("circle")
.attr("cx", (d) => x(d[xCol]))
.attr("cy", (d) => y(d[yCol]))
.attr("r", radius)
//.attr("opacity", 0.7)
.attr("fill", UIpaleDarkCyan)
.attr("stroke", "white")
.attr("stroke-width", 0.1);
//Fill function
//VA institutions popout
const dotsVA = svg
.append("g")
.attr("transform", `translate(${margin_sc.left}, ${margin_sc.top})`)
.selectAll("circle")
.data(
schoolData.filter(function (d) {
return d.stateVA === "Virginia";
})
)
.join("circle")
.attr("cx", (d) => x(d.li_pep_pct))
.attr("cy", (d) => y(d.emi))
.attr("r", 3.5)
.attr("fill", (d) => colorFunc(d.Control))
.attr("opacity", 1)
.attr("stroke", "black")
.attr("stroke-width", 0.5);
const dotsVAtext = dotsVA.append("title").text((d) => `${d.Institution}\n`);
dotsVAtext
.append("subtitle")
.text(
(d) =>
`EMI Rank in VA: ${d.emi_rank_va}\nEMI Rank Nationally: ${d.emi_rank}\nTotal Undergraduates: ${d.tot_ug}\nControl: ${d.Control}\nCarnegie Classification 2018: ${d.BASIC2018}\nCarnegie Locale: ${d.LOCALE_ABBV}`
);
dotsVA
.on("mouseover", function () {
d3.select(this)
.attr("fill", "#EC008B")
.attr("stroke", "black")
.attr("stroke-width", 1);
})
.on("mouseout", function () {
d3.select(this)
.attr("fill", (d) => colorFunc(d.Control))
.attr("stroke", "black")
.attr("stroke-width", 0.5);
});
//Event gives the coordinates of the brush box
function onBrush(event) {
const [[x1, y1], [x2, y2]] = event.selection;
//True if dot in brush box, false otherwise
function isBrushed(d) {
const cx = x(d[xCol]);
const cy = y(d[yCol]);
return cx >= x1 && cx <= x2 && cy >= y1 && cy <= y2;
}
//Dot color
dots.attr("fill", (d) => (isBrushed(d) ? UIpaleCyan : palegrey));
dotsVA
.attr("fill", (d) =>
isBrushed(d) ? colorFunc(d.Control) : colorFunc(d.Control)
)
.attr("opacity", (d) => (isBrushed(d) ? 1 : 0.5))
.attr("stroke-width", (d) => (isBrushed(d) ? 1 : 0))
.attr("stroke", (d) => (isBrushed ? "black" : palegrey));
//Updating the bar chart (I hope)
svg.property("value", schoolData.filter(isBrushed)).dispatch("input");
}
//Finishing the Brushing
function onEnd(event) {
if (event.selection === null) {
dots.attr("fill", UIpaleDarkCyan);
dotsVA
.attr("stroke-width", 0.5)
.attr("stroke", "black")
.attr("opacity", 1)
.attr("fill", (d) => colorFunc(d.Control));
svg.property("value", initialValue).dispatch("input");
}
}
return svg.node();
}