Public
Edited
May 21
1 fork
1 star
Insert cell
Insert cell
Insert cell
Insert cell
function addRotateDrag(chart, dim, initialAngle = 0) {
const state = {r: initialAngle, x: dim.width/2, y: dim.height/2}

chart.attr("transform", `translate(${[dim.width / 2, dim.height / 2]}) rotate(${state.r})`);

chart.call(d3.drag()
.on("start", function(evt) {
const ctm = this.getCTM();
const x = evt.x - ctm.e;
const y = evt.y - ctm.f;
state.startAngle = Math.atan2(y, x) * 180 / Math.PI;
})
.on("drag", function(evt) {
const ctm = this.getCTM();
const x = evt.x - ctm.e;
const y = evt.y - ctm.f;
const currentAngle = Math.atan2(y, x) * 180 / Math.PI;
state.r += currentAngle - state.startAngle;
state.startAngle = currentAngle;
chart.attr("transform", `translate(${[dim.width / 2, dim.height / 2]}) rotate(${state.r})`);
}));
}
Insert cell
{
const dim = {width: 1100, height: 1000};
const margin = {top: 100, right: 10, bottom: 300, left: 150};
const cht = {w: dim.width - margin.left - margin.right, h: dim.height - margin.top - margin.bottom};

const chart = d3.select("svg")
.style("font", "10px sans-serif")
.append("g")
.attr("transform", `translate(${dim.width/2}, ${dim.height/2})`);

chart.append("circle")
.attr("r", cht.h *.9)
.attr("fill", "white")
.style("cursor", "grab");

const tabular = d3.csvParse(await file.text())

const root = d3.stratify()
.id(d => d.node)
.parentId(d => d.parent)(tabular);

//root.count();
root.sum(d => 1) // number of nodes
// root.sort((a,b) => b.height - a.height || a.value - b.value);

d3.partition().size([2 * Math.PI, 480])(root);

draw(chart.append("g").attr("class", "partition"), root.descendants());
d3.selectAll("g.slice").filter(d => !d.data.name || d.height === 0).lower();

addRotateDrag(chart, dim, -169);

return "page";
}
Insert cell
Kellner.csv
Type SQL, then Shift-Enter. Ctrl-space for more options.

Insert cell
function draw(g, data) {
const colorScale = d3.scaleSequential(d3.interpolateYlGnBu)
.domain([30, -40]);

const arc = d3.arc().startAngle(d => d.x0)
.endAngle(d => d.x1)
.innerRadius(d => d.y0)
.outerRadius(d => d.y1);
const start = arc.startAngle();
const end = arc.endAngle();

const cell = g.selectAll("g.slice")
.data(data)
.join("g")
.attr("class", "slice")
.style("pointer-events", "none");

cell.append('path')
.attr('d', arc)
.attr("fill", d => d.height != 0 ? colorScale(d.height) : 'orange');

// get the arc that runs between the inner and outer radius of the path above
const midArc = d3.arc()
.startAngle(d => d.x0 - 90 * Math.PI / 180) // to fit all text
.endAngle(d => d.x1 + 90 * Math.PI / 180)
.innerRadius(d => (d.y0 + d.y1) / 2)
.outerRadius(d => (d.y0 + d.y1) / 2);

cell.append('path')
.attr('id', d => `${d.data.name}`)
.attr('d', midArc)
.style("fill", "none")
.style("stroke", "none");

cell.filter(d => d.height !== 0)
.append("text")
.attr("dy", 2)
.append("textPath").attr("startOffset", "25%").attr("href", d => `#${d.data.name}`)
.text(d => d.data.name)
.style("text-anchor", "middle");

cell.filter(d => d.height === 0)
.append("text")
.text(d => d.data.name)
.style("text-anchor", "start")
.attr("x", 15)
.attr("transform", d =>
`translate(${arc.centroid(d)}) rotate(${(180/Math.PI * (start(d) + end(d))/2 - 90)})`)
.style("pointer-events", "none")
.style("alignment-baseline", "middle");
}
Insert cell
file = FileAttachment("Kellner.csv")
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