287 lines
6.8 KiB
JavaScript

function drawBar(id,data) {
//console.log(id);
const svg = d3.select(`${id}`);
const backgroundwidth = +svg.attr("width");
const backgroundradius = 10;
const width = 600; //+svg.attr("width");
const height = 200; //+svg.attr("height");
const margin = { top: 40, right: 30, bottom: 60, left: 30 };
// Draw the white background
// Draw the rounded rectangle
svg.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", backgroundwidth)
.attr("height", height)
.attr("rx", backgroundradius)
.attr("ry", backgroundradius)
.attr("fill", "white");
// .attr("stroke", "white")
// .attr("stroke-width", 2);
//const data = {
/*
data = {
"-3": 0,
"-2": 1,
"-1": 3,
"0": 0,
"1": 2,
"2": 0,
"3": 10
};
*/
//console.log(data);
const dataArray = Object.entries(data).map(([key, count]) => ({
key: +key,
count: count
}));
const x = d3.scaleBand()
.domain([-3,-2,-1,0,1,2,3])
.range([margin.left, width - margin.right])
.padding(0);
const y = d3.scaleLinear()
.domain([0, d3.max(dataArray, d => d.count)])
.nice()
.range([height - margin.bottom, margin.top]);
// X Axis
svg.append("g")
.attr("transform", `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(x)
.tickSize(0))
.selectAll("text")
.attr("x", 0)
.attr("y", 10)
.style("text-anchor", "middle")
.style("font-size", "12px");
// Add the required ticks
const xTicks = d3.scaleLinear()
.domain([-3.5, 3.5])
.range([margin.left, width - margin.right]);
svg.append("g")
.attr("transform", `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(xTicks).tickValues(d3.range(-3.5, 4, 1)))
.selectAll("text")
.style("visibility", "hidden"); // This hides the labels
// Y Axis
//svg.append("g")
// .attr("transform", `translate(${margin.left},0)`)
// .call(d3.axisLeft(y).ticks(5));
// Bars
svg.selectAll(".bar")
.data(dataArray)
.enter()
.append("rect")
.attr("class", "bar")
.attr("x", d => x(d.key))
.attr("y", d => y(d.count))
.attr("width", x.bandwidth())
.attr("height", d => y(0) - y(d.count))
.attr("fill", "#003c4b"); // From brand guidelines
// Labels
svg.selectAll(".label")
.data(dataArray)
.enter()
.append("text")
.attr("x", d => x(d.key) + x.bandwidth() / 2)
.attr("y", d => d.count > 0 ? y(d.count) - 5 : y(0))
.attr("text-anchor", "middle")
.attr("font-size", "12px")
.attr("font-style", "italic")
.attr("fill", "black")
.text(d => d.count > 0 ? d.count : "");
// Average line
const format = d3.format(".2f");
const totalCount = d3.sum(dataArray, d => d.count);
const weightedSum = d3.sum(dataArray, d => d.key * d.count);
average = totalCount > 0 ? weightedSum / totalCount : 0;
const xLinear = d3.scaleLinear()
.domain([-3, 3])
.range([margin.left + x.bandwidth() / 2, width - margin.right - x.bandwidth() / 2]);
const avgX = xLinear(average);
//const avgX = xLinear(-0.50); // Temporary for testing
svg.append("line")
.attr("x1", avgX)
.attr("x2", avgX)
.attr("y1", y(0)+20)
.attr("y2", y(d3.max(dataArray, d => d.count)) - 20)
.attr("stroke", "#e40074") // From brand guidelines
.attr("stroke-width", 4)
.attr("stroke-dasharray", "10,4");
/*
svg.append("text")
.attr("x", avgX)
.attr("y", y(d3.max(dataArray, d => d.count)) - 30)
.attr("text-anchor", "middle")
.attr("fill", "#e40074")
.attr("font-size", "12px")
.text("Average ("+average+")");
*/
// Title beneath the graph
/*
svg.append("text")
.attr("x", width / 2)
.attr("y", height - 10) // Just below the x-axis
.attr("text-anchor", "middle")
.attr("font-size", "14px")
.attr("fill", "black")
.text("Demo bar graph");
*/
// Add info to right of bar chart
// Oblong settings
const boxx = 610;
const boxy = 60;
const boxwidth = 170;
const boxheight = 80;
const cornerRadius = 10;
// Draw the rounded rectangle
svg.append("rect")
.attr("x", boxx)
.attr("y", boxy)
.attr("width", boxwidth)
.attr("height", boxheight)
.attr("rx", cornerRadius)
.attr("ry", cornerRadius)
.attr("fill", "white")
.attr("stroke", "#333")
.attr("stroke-width", 2);
// Draw the horizontal divider
svg.append("line")
.attr("x1", boxx)
.attr("x2", boxx + boxwidth)
.attr("y1", boxy + boxheight / 2)
.attr("y2", boxy + boxheight / 2)
.attr("stroke", "#333")
.attr("stroke-width", 1);
// Add text to the top half
svg.append("text")
.attr("x", boxx + boxwidth / 2)
.attr("y", boxy + boxheight / 4)
.attr("text-anchor", "middle")
.attr("dominant-baseline", "middle")
.attr("font-size", "20px")
.attr("font-weight", "400") // 400 is normal, 700 is bold, 900 is extra-bold
.attr("font-family", "sans-serif")
.text("Average: ")
.append(function() {
return document.createElementNS("http://www.w3.org/2000/svg", "tspan");
})
.text(average.toFixed(1))
.attr("class", "average")
.attr("fill", "#e40074")
.attr("font-weight", "900");
// .text("Average : "+average.toFixed(2));
// Get the score gap:
let maxKey = null;
let minKey = null;
for (const [key, value] of Object.entries(data)) {
if (value > 0) {
const numKey = Number(key);
//console.log(numkey);
if (maxKey === null || numKey > maxKey) {
maxKey = numKey;
}
if (minKey === null || numKey < minKey) {
minKey = numKey;
}
}
}
scoregap = maxKey - minKey+1;
// Add text to the bottom half
svg.append("text")
.attr("x", boxx + boxwidth / 2)
.attr("y", boxy + 3 * boxheight / 4)
.attr("text-anchor", "middle")
.attr("dominant-baseline", "middle")
.attr("font-size", "20px")
.attr("font-weight", "400") // 400 is normal, 700 is bold, 900 is extra-bold
.attr("font-family", "sans-serif")
.text("Score gap: ")
.append(function() {
return document.createElementNS("http://www.w3.org/2000/svg", "tspan");
})
.text(scoregap)
.attr("font-weight", "900");
// .text("Score Gap : "+scoregap);
return average; //Needed for the next layer up in the analysis
}
function doBarData(id,qid) {
return fetch('get_qid_counts.php', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({ qid })
})
.then(response => {
if (!response.ok) {
throw new Error('Network response was not OK');
}
return response.json();
})
.then(data => {
if (data.error) {
throw new Error(data.error);
}
// console.log(data);
// console.log(data[0][Object.keys(data[0])[0]]);
/* */
bardata = {
"-3": data[0][Object.keys(data[0])[0]],
"-2": data[1][Object.keys(data[1])[0]],
"-1": data[2][Object.keys(data[2])[0]],
"0": data[3][Object.keys(data[3])[0]],
"1": data[4][Object.keys(data[4])[0]],
"2": data[5][Object.keys(data[5])[0]],
"3": data[6][Object.keys(data[6])[0]],
};
//console.log(bardata);
drawBar(id,bardata);
// return data; // Should be an array like [{ value: -3, count: 2 }, ..., { value: 3, count: 5 }]
});
}