beeswarm = (data, date, type, toColor) => {
const radius = 2.5;
const padding = 1;
const size = { w: 600, h: 1000 };
const margin = { top: 100, bottom: 30, left: 130, right: 100 };
const colors = "#e6ab02";
const xScale = d3
.scaleTime()
.range([0, size.w])
.domain(d3.extent(data, (d) => d.Data));
const colorScale = d3
.scaleSequential()
.domain([0, d3.max(data, (d) => d[toColor])])
.interpolator(d3.interpolateHcl("pink", "darkblue"));
let category = [...new Set(data.map((d) => d[type]))];
category.reverse();
const svg = d3.select(
DOM.svg(
size.w + margin.left + margin.right,
size.h + margin.top + margin.bottom
)
);
const g = svg
.append("g")
.attr("transform", `translate(${margin.left}, ${margin.top})`);
const xAxis = d3.axisBottom().scale(xScale).ticks(d3.timeYear);
g.append("g")
.attr("transform", `translate(0, ${size.h - margin.top})`)
.call(xAxis)
.select(".domain")
.remove();
const yScale = d3
.scalePoint()
.domain(category)
.range([0, size.h - margin.top - margin.bottom]);
const yAxis = d3.axisLeft().scale(yScale);
g.append("g")
.attr("transform", `translate(0,0)`)
.call(yAxis)
.select(".domain")
.remove();
const toolTip = d3.select("body").append("div").attr("class", "toolTip");
const formatDate = d3.timeFormat("%d %b %Y");
function nodeMouseOver(event, d) {
toolTip
.style("left", event.pageX + 18 + "px")
.style("top", event.pageY + 18 + "px")
.style("display", "block")
.html(
`${d["Città"].toUpperCase()} - <em>${formatDate(
d["Data"]
)}</em><br><strong>${d.Title}</strong><br>${d.Text.slice(0, 200)}...`
);
d3.select(event.target).style("cursor", "pointer");
d3.select(event.target).transition().attr("fill", "#A8234E").attr("r", 10);
}
function nodeMouseOut(event, d) {
toolTip.style("display", "none");
d3.select(event.target).style("cursor", "default");
d3.select(event.target)
.transition()
.attr("fill", (d) => colorScale(d[toColor]))
.attr("r", radius);
}
const bee = g
.append("g")
.attr("class", "bee")
.attr("transform", `translate(0,0)`);
const nodes = bee
.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("class", "node")
.attr("cy", (d) => {
d.y = size.h / 2;
return d.y;
})
.attr("cx", (d) => {
d.x = xScale(d[date]);
return d.x;
})
.attr("r", radius)
.attr("fill", (d) => colorScale(d[toColor]))
.attr("fill-opacity", 0.7)
.on("mousemove", nodeMouseOver)
.on("mouseout", nodeMouseOut);
let simulation = d3
.forceSimulation()
.force("collide", d3.forceCollide(radius + padding))
.force("x", d3.forceX((d) => d.x).strength(1))
.alphaMin(0.00001);
if (category) {
simulation.force("y", d3.forceY((d) => yScale(d[type])).strength(1));
} else {
simulation.force("y", d3.forceY((d) => d.y).strength(1));
}
simulation.nodes(data).on("tick", () => {
nodes.attr("cx", (d) => d.x).attr("cy", (d) => d.y);
});
return svg.node();
}