Cheese diagram in d3.js
Categories:
Crafting a 'Cheese Diagram' with D3.js: A Sunburst Visualization Guide

Explore how to create an interactive 'cheese diagram' (sunburst chart) using D3.js, perfect for visualizing hierarchical data with a radial layout.
The 'cheese diagram,' more formally known as a sunburst chart, is a powerful data visualization technique for displaying hierarchical data. It uses a radial layout to show the relationships between different levels of a hierarchy, where each level is represented by a ring, and segments within the ring represent categories. D3.js, a JavaScript library for manipulating documents based on data, provides the perfect toolkit for building such complex and interactive visualizations. This article will guide you through the process of creating a sunburst diagram, from data preparation to D3.js implementation.
Understanding Sunburst Diagrams and Their Data Structure
A sunburst diagram is essentially a multi-level pie chart. The innermost circle represents the root of the hierarchy, and subsequent rings represent deeper levels. The size of each arc segment typically corresponds to a quantitative value, allowing for easy comparison of sibling nodes. For D3.js to render a sunburst diagram effectively, your data needs to be structured hierarchically, often as a nested JSON object. Each node in the hierarchy should have a name
and, if it's a leaf node, a value
. Intermediate nodes will contain a children
array.
graph TD A[Root] --> B[Category 1] A --> C[Category 2] B --> D[Subcategory 1.1] B --> E[Subcategory 1.2] C --> F[Subcategory 2.1] D --> G[Item 1.1.1 (Value)] D --> H[Item 1.1.2 (Value)] E --> I[Item 1.2.1 (Value)] F --> J[Item 2.1.1 (Value)]
Hierarchical data structure for a sunburst diagram
{
"name": "Total",
"children": [
{
"name": "Category A",
"children": [
{"name": "Sub A1", "value": 10},
{"name": "Sub A2", "value": 20}
]
},
{
"name": "Category B",
"children": [
{"name": "Sub B1", "value": 15},
{"name": "Sub B2", "value": 25},
{"name": "Sub B3", "value": 5}
]
}
]
}
Example of hierarchical JSON data for a sunburst chart
Setting Up Your D3.js Environment and Scales
Before drawing the sunburst, you'll need to set up your SVG container, define the dimensions, and establish D3's hierarchical layout functions. The d3.partition()
layout is crucial for sunburst diagrams, as it computes the angular and radial positions for each node. You'll also need d3.arc()
to generate the SVG path data for each segment and d3.scaleOrdinal()
with d3.schemeCategory10
(or a custom color scheme) for coloring the segments.
const width = 920;
const height = 920;
const radius = Math.min(width, height) / 6;
const svg = d3.select("#chart")
.append("svg")
.attr("viewBox", `-${width / 2} -${height / 2} ${width} ${height}`)
.style("font", "10px sans-serif");
const color = d3.scaleOrdinal(d3.schemeCategory10);
const partition = d3.partition()
.size([2 * Math.PI, radius * radius]);
const arc = d3.arc()
.startAngle(d => d.x0)
.endAngle(d => d.x1)
.padAngle(d => Math.min((d.x1 - d.x0) / 2, 0.005))
.padRadius(radius * 1.5)
.innerRadius(d => Math.sqrt(d.y0))
.outerRadius(d => Math.sqrt(d.y1) - 1);
// Data loading and processing would follow here
// d3.json("data.json").then(data => { ... });
Initial D3.js setup for SVG, scales, and layout functions
viewBox
attribute on the SVG is powerful for responsive design. By setting it to -${width / 2} -${height / 2} ${width} ${height}
, we center the origin (0,0) of the SVG in the middle of the container, which is ideal for radial charts.Drawing the Sunburst Segments and Adding Interactivity
Once your data is loaded and processed by d3.hierarchy()
and partition()
, you can bind it to SVG <path>
elements. Each path will represent a segment of your sunburst. To make the chart interactive, you'll typically add event listeners for mouseover
and click
events. mouseover
can display details about the hovered segment, while click
can be used to 'zoom in' on a particular segment, re-rendering the chart to show its children as the new root.
function drawSunburst(data) {
const root = d3.hierarchy(data)
.sum(d => d.value)
.sort((a, b) => b.value - a.value);
const nodes = partition(root).descendants();
svg.append("g")
.selectAll("path")
.data(nodes)
.join("path")
.attr("fill", d => {
if (!d.depth) return "#ccc"; // Root node color
while (d.depth > 1) d = d.parent; // Color by top-level parent
return color(d.data.name);
})
.attr("fill-opacity", d => d.children ? 0.6 : 0.4)
.attr("d", arc)
.on("mouseover", (event, d) => {
// Display tooltip or highlight logic
console.log(`Hovered: ${d.data.name}, Value: ${d.value}`);
})
.on("click", (event, d) => {
// Implement zoom functionality
console.log(`Clicked: ${d.data.name}`);
})
.append("title")
.text(d => `${d.ancestors().map(d => d.data.name).reverse().join("/")}\nValue: ${d.value}`);
// Add text labels for segments (optional, can be complex for small segments)
svg.append("g")
.attr("pointer-events", "none")
.attr("text-anchor", "middle")
.selectAll("text")
.data(nodes)
.join("text")
.attr("transform", d => {
const x = (d.x0 + d.x1) / 2 * 180 / Math.PI;
const y = (Math.sqrt(d.y0) + Math.sqrt(d.y1)) / 2;
return `rotate(${x - 90}) translate(${y},0) rotate(${x > 90 ? -180 : 0})`;
})
.attr("dy", "0.35em")
.text(d => d.data.name);
}
// Call this function after loading your data
// d3.json("data.json").then(drawSunburst);
D3.js code to draw sunburst segments and add basic interactivity
Enhancing the 'Cheese Diagram' with Zoom and Tooltips
A key feature of effective sunburst diagrams is the ability to interactively explore the hierarchy. Implementing a 'zoom' feature allows users to click on a segment and have it become the new root, revealing more detail about its children. This involves transitioning the x0
, x1
, y0
, and y1
properties of the arcs. Tooltips provide on-demand information without cluttering the main visualization. You can create a simple HTML div
element and update its content and position on mouseover
and mouseout
events.
sequenceDiagram participant User participant Browser participant D3.js User->>Browser: Mouseover segment Browser->>D3.js: Trigger 'mouseover' event D3.js->>Browser: Update tooltip content and position User->>Browser: Click segment Browser->>D3.js: Trigger 'click' event D3.js->>D3.js: Recalculate partition layout for new root D3.js->>Browser: Transition arc paths and text labels Browser->>User: Display zoomed view
Interaction flow for sunburst zoom and tooltip
Implementing zoom requires updating the arc
generator's innerRadius
and outerRadius
based on the clicked node's position in the hierarchy. D3's transition()
method is essential for smooth animations, making the user experience seamless. For tooltips, you'll typically create a div
element in your HTML, style it with CSS, and then use JavaScript to show/hide and populate it with data from the hovered segment.
d3.partition()
layout is re-calculated or adjusted relative to the new 'root' node. Incorrect scaling can lead to distorted or overlapping segments.