mirror of https://github.com/CIRCL/AIL-framework
fix: [chat d3 heatmap] add missing function
parent
fa4f1269cd
commit
235a913865
|
@ -0,0 +1,224 @@
|
|||
//Requirement: - D3v5
|
||||
// - jquery
|
||||
// data input: var data = [{"count":0,"date":"2023-11-20","day":0,"hour":0}
|
||||
// based on gist nbremer/62cf60e116ae821c06602793d265eaf6
|
||||
|
||||
// container_id = #heatmapweekhour
|
||||
|
||||
const create_heatmap_week_hour = (container_id, data, options) => {
|
||||
|
||||
|
||||
var days = ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
|
||||
times = d3.range(24);
|
||||
|
||||
var margin = {
|
||||
top: 80,
|
||||
right: 50,
|
||||
bottom: 20,
|
||||
left: 50
|
||||
};
|
||||
|
||||
var width = Math.max(Math.min(window.innerWidth, 1000), 500) - margin.left - margin.right - 20,
|
||||
gridSize = Math.floor(width / times.length),
|
||||
height = gridSize * (days.length + 2);
|
||||
|
||||
var heatmap_font_size = width * 62.5 / 900;
|
||||
|
||||
//SVG container
|
||||
var svg = d3.select(container_id)
|
||||
.append("svg")
|
||||
.attr("width", width + margin.left + margin.right)
|
||||
.attr("height", height + margin.top + margin.bottom)
|
||||
.append("g")
|
||||
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
|
||||
|
||||
// create a tooltip
|
||||
const tooltip = d3.select(container_id)
|
||||
.append("div")
|
||||
.style("opacity", 0)
|
||||
.attr("class", "tooltip")
|
||||
.style("background-color", "white")
|
||||
.style("border", "solid")
|
||||
.style("border-width", "2px")
|
||||
.style("border-radius", "5px")
|
||||
.style("padding", "5px")
|
||||
|
||||
// Three function that change the tooltip when user hover / move / leave a cell
|
||||
const mouseover = function(d) {
|
||||
tooltip.style("opacity", 1)
|
||||
d3.select(this)
|
||||
.style("stroke", "black")
|
||||
//.style("stroke-opacity", 1)
|
||||
|
||||
tooltip.html(d.date + " " + d.hour + "-" + (d.hour + 1) + "h: <b>" + d.count + "</b> messages")
|
||||
}
|
||||
const mouseleave = function(d) {
|
||||
tooltip.style("opacity", 0)
|
||||
d3.select(this)
|
||||
.style("stroke", "white")
|
||||
//.style("stroke-opacity", 0.8)
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////// Draw Heatmap /////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
var colorScale = d3.scaleLinear()
|
||||
.domain([0, d3.max(data, function (d) {
|
||||
return d.count;
|
||||
}) / 2, d3.max(data, function (d) {
|
||||
return d.count;
|
||||
})])
|
||||
.range(["#FFFFF6", "#3E9583", "#1F2D86"])
|
||||
//.interpolate(d3.interpolateHcl);
|
||||
|
||||
var dayLabels = svg.selectAll(".dayLabel")
|
||||
.data(days)
|
||||
.enter().append("text")
|
||||
.text(function (d) {
|
||||
return d;
|
||||
})
|
||||
.attr("x", 0)
|
||||
.attr("y", function (d, i) {
|
||||
return i * gridSize;
|
||||
})
|
||||
.style("text-anchor", "end")
|
||||
.attr("transform", "translate(-36, -11)")
|
||||
.attr("class", function (d, i) {
|
||||
return ((i >= 0 && i <= 4) ? "dayLabel mono axis axis-workweek" : "dayLabel mono axis");
|
||||
})
|
||||
.style("font-size", heatmap_font_size + "%");
|
||||
|
||||
var timeLabels = svg.selectAll(".timeLabel")
|
||||
.data(times)
|
||||
.enter().append("text")
|
||||
.text(function (d) {
|
||||
return d;
|
||||
})
|
||||
.attr("x", function (d, i) {
|
||||
return i * gridSize;
|
||||
})
|
||||
.attr("y", 0)
|
||||
.style("text-anchor", "middle")
|
||||
.attr("transform", "translate(-" + gridSize / 2 + ", -36)")
|
||||
.attr("class", function (d, i) {
|
||||
return ((i >= 8 && i <= 17) ? "timeLabel mono axis axis-worktime" : "timeLabel mono axis");
|
||||
})
|
||||
.style("font-size", heatmap_font_size + "%");
|
||||
|
||||
var heatMap = svg.selectAll(".hour")
|
||||
.data(data)
|
||||
.enter().append("rect")
|
||||
.attr("x", function (d) {
|
||||
return (d.hour - 1) * gridSize;
|
||||
})
|
||||
.attr("y", function (d) {
|
||||
return (d.day - 1) * gridSize;
|
||||
})
|
||||
.attr("class", "hour bordered")
|
||||
.attr("width", gridSize)
|
||||
.attr("height", gridSize)
|
||||
.style("stroke", "white")
|
||||
.style("stroke-opacity", 0.6)
|
||||
.style("fill", function (d) {
|
||||
return colorScale(d.count);
|
||||
})
|
||||
.on("mouseover", mouseover)
|
||||
.on("mouseleave", mouseleave);
|
||||
|
||||
//Append title to the top
|
||||
svg.append("text")
|
||||
.attr("class", "title")
|
||||
.attr("x", width / 2)
|
||||
.attr("y", -60)
|
||||
.style("text-anchor", "middle")
|
||||
.text("Chat Messages");
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
//////////////// Create the gradient for the legend ///////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//Extra scale since the color scale is interpolated
|
||||
var countScale = d3.scaleLinear()
|
||||
.domain([0, d3.max(data, function (d) {
|
||||
return d.count;
|
||||
})])
|
||||
.range([0, width])
|
||||
|
||||
//Calculate the variables for the temp gradient
|
||||
var numStops = 10;
|
||||
countRange = countScale.domain();
|
||||
countRange[2] = countRange[1] - countRange[0];
|
||||
countPoint = [];
|
||||
for (var i = 0; i < numStops; i++) {
|
||||
countPoint.push(i * countRange[2] / (numStops - 1) + countRange[0]);
|
||||
}
|
||||
|
||||
//Create the gradient
|
||||
svg.append("defs")
|
||||
.append("linearGradient")
|
||||
.attr("id", "legend-heatmap")
|
||||
.attr("x1", "0%").attr("y1", "0%")
|
||||
.attr("x2", "100%").attr("y2", "0%")
|
||||
.selectAll("stop")
|
||||
.data(d3.range(numStops))
|
||||
.enter().append("stop")
|
||||
.attr("offset", function (d, i) {
|
||||
return countScale(countPoint[i]) / width;
|
||||
})
|
||||
.attr("stop-color", function (d, i) {
|
||||
return colorScale(countPoint[i]);
|
||||
});
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////// Draw the legend ////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
var legendWidth = Math.min(width * 0.8, 400);
|
||||
//Color Legend container
|
||||
var legendsvg = svg.append("g")
|
||||
.attr("class", "legendWrapper")
|
||||
.attr("transform", "translate(" + (width / 2) + "," + (gridSize * days.length) + ")"); // 319
|
||||
|
||||
//Draw the Rectangle
|
||||
legendsvg.append("rect")
|
||||
.attr("class", "legendRect")
|
||||
.attr("x", -legendWidth / 2)
|
||||
.attr("y", 0)
|
||||
//.attr("rx", hexRadius*1.25/2)
|
||||
.attr("width", legendWidth)
|
||||
.attr("height", 10)
|
||||
.style("fill", "url(#legend-heatmap)");
|
||||
|
||||
//Append title
|
||||
legendsvg.append("text")
|
||||
.attr("class", "legendTitle")
|
||||
.attr("x", 0)
|
||||
.attr("y", -10)
|
||||
.style("text-anchor", "middle")
|
||||
.text("Number of Messages");
|
||||
|
||||
//Set scale for x-axis
|
||||
var xScale = d3.scaleLinear()
|
||||
.range([-legendWidth / 2, legendWidth / 2])
|
||||
.domain([0, d3.max(data, function (d) {
|
||||
return d.count;
|
||||
})]);
|
||||
|
||||
//Define x-axis
|
||||
var xAxis = d3.axisBottom(xScale)
|
||||
//.orient("bottom")
|
||||
.ticks(5);
|
||||
//.tickFormat(formatPercent)
|
||||
|
||||
//Set up X axis
|
||||
legendsvg.append("g")
|
||||
.attr("class", "axis")
|
||||
.attr("transform", "translate(0," + (10) + ")")
|
||||
.call(xAxis);
|
||||
|
||||
|
||||
// return svg ???
|
||||
// return svg
|
||||
|
||||
}
|
Loading…
Reference in New Issue