Public
Edited
Dec 2, 2023
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
groupedData = d3.rollup(
schedules,
(v) => d3.sum(v, (d) => d.diff),
(d) => d.content
)
Insert cell
Array.from(groupedData)
Insert cell
config = ({
w: width,
h: 600,
mx: 50,
my: 50
})
Insert cell
DateTime = luxon.DateTime
Insert cell
{
const scaleCategX = d3
.scaleBand()
.domain(Object.keys(groupedData))
.range([0, 300]);
return Array.from(groupedData.keys());
}
Insert cell
chart = {
const svg = d3.create("svg").attr("width", config.w).attr("height", config.h);
const scaleTimeY = d3
.scaleTime()
.domain([essentials.start, essentials.end])
.range([config.my, config.h - config.my * 2]);
const axisY = d3
.axisLeft(scaleTimeY)
.tickFormat(d3.timeFormat("%H:%M"))
.ticks(d3.timeHour);
const gAxisY = svg.append("g");
gAxisY.attr("transform", `translate(${config.mx},0)`);
gAxisY.call(axisY);
gAxisY
.selectAll(".tick > line")
.attr("x1", -config.w + config.mx * 2)
.attr("transform", `translate(${config.w - config.mx * 2})`);
const chartMain = svg
.append("g")
.attr("transform", `translate(${config.mx},0)`);
const scaleColor = d3
.scaleOrdinal(d3.schemeCategory10)
.domain(Array.from(groupedData.keys()));

const chartCateg = svg
.append("g")
.attr("transform", `translate(${config.mx + 45},0)`);
const scaleCategX = d3
.scaleBand()
.domain(Array.from(groupedData.keys()))
.range([0, 150])
.paddingInner(0.5);

const gLegend = svg.append("g");
gLegend.attr("transform", `translate(${config.w - config.mx},${config.my})`);

function render() {
chartMain
.selectAll(".main-chart-item")
.data(schedules, (d) => d.id)
.join((enter) =>
enter
.append("rect")
.attr("class", "main-chart-item")
.attr("rx", 3)
.attr("ry", 3)
.attr("fill", (d) => scaleColor(d.content))
.attr("opacity", 0.5)
.attr("transform", (d) => `translate(0, ${scaleTimeY(d.time)})`)
.attr("width", (d) => 30)
.attr("height", (d) => scaleTimeY(d.nextTime) - scaleTimeY(d.time))
);
chartCateg
.selectAll(".categ-chart-item")
.data(schedules)
.join((enter) =>
enter
.append("rect")
.attr("class", "categ-chart-item")
.attr("rx", 3)
.attr("ry", 3)
.attr("fill", (d) => scaleColor(d.content))
.attr("width", (d) => scaleCategX.bandwidth())
.attr("height", (d) => scaleTimeY(d.nextTime) - scaleTimeY(d.time))
.attr(
"transform",
(d) => `translate(${scaleCategX(d.content)},${scaleTimeY(d.time)})`
)
);
gLegend
.selectAll(".legend-item")
.data(Array.from(groupedData))
.join((enter) =>
enter
.append("g")
.attr("class", "legend-item")
.attr("transform", (d, i) => `translate(-30, ${i * 30})`)
.attr("text-anchor", "end")
.attr("dominant-baseline", "hanging")
.call((l) =>
l
.append("text")
.text(
([k, v]) =>
`${k} - ${luxon.Duration.fromMillis(v).toFormat(
"hh'h'mm'm'"
)}`
)
)
.call((l) =>
l
.append("rect")
.attr("width", "10")
.attr("height", "10")
.attr("fill", ([k, v]) => scaleColor(k))
)
);
}
render();
return Object.assign(svg.node(), { render });
}
Insert cell
chartArc = {
const svg = d3.create("svg").attr("width", config.w).attr("height", config.h);
const scaleTimeRad = d3
.scaleRadial()
.domain([essentials.start, essentials.end])
.range([0, Math.PI * 2]);
const gArc = svg.append("g");
const arcRadii = d3.max([config.w, config.h]) / 2;
gArc.attr("transform", `translate(${config.w / 2},${config.h / 2})`);
const scaleColor = d3
.scaleOrdinal()
.domain(Array.from(groupedData.keys()))
.range(d3.schemeTableau10);
const scaleTypeY = d3
.scaleBand()
.domain(Array.from(groupedData.keys()))
.range([arcRadii / 3, arcRadii / 5]);
function render() {
const arc = d3
.arc()
.startAngle((d) => scaleTimeRad(d.time))
.endAngle((d) => scaleTimeRad(d.nextTime))
.innerRadius(arcRadii / 3)
.outerRadius(arcRadii / 5);
// .innerRadius((d) => scaleTypeY(d.content))
// .outerRadius((d) => scaleTypeY(d.content) + scaleTypeY.bandwidth());
gArc
.selectAll(".arc-item")
.data(schedules, (d) => d.id)
.join((enter) =>
enter.append("g").call((g) =>
g
.append("path")
.attr("d", arc)
.attr("fill", (d) => scaleColor(d.content))
)
);
}
render();
return Object.assign(svg.node(), { render });
}
Insert cell
luxon = require("luxon@3/build/global/luxon.min.js").catch(
() => window["luxon"]
)
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more