Public
Edited
Jun 16
1 star
Insert cell
Insert cell
chart = {
// Specify the chart’s dimensions.
const width = 820;
const height = width;
const cx = width * 0.5; // adjust as needed to fit
const cy = height * 0.55; // adjust as needed to fit
const radius = Math.min(width, height) / 2.75 - 80;

// Define your data here
const data = {
"name": "Reality Experience",
"studies": 117,
"children": [
{
"name": "AR",
"studies": 14, // Studies count for AR
"children": [
{
"name": "BEW",
"children": [
{
"name": "Route Choice & Landmark Use (3)",
},
{
"name": "Accessibility Features (2)",
},
{
"name": "Obstacle Avoidance & Terrain (2)",
},
{
"name": "Wayfinding for Special Populations (1)",
}
]
},
{
"name": "PCB",
"children": [
{
"name": "Gap Acceptance & Timing (1)"
}
]
},
{
"name": "PIB",
"children": [
{
"name": "Automated Vehicle Interaction (2)"
},
{
"name": "Eye Contact & Social Cues (1)"
},
{
"name": "Conflict or Negotiation Scenarios (2)"
}
]
}
]
},
{
"name": "MR",
"studies": 8, // Studies count for MR
"children": [
{
"name": "BEW",
"children": [
{
"name": "Route Choice & Landmark Use (2)"
},
{
"name": "Obstacle Avoidance & Terrain (1)"
}
]
},
{
"name": "PCB",
"children": [
{
"name": "Gap Acceptance & Timing (1)"
}
]
},
{
"name": "PIB",
"children": [
{
"name": "Automated Vehicle Interaction (1)"
}
]
},
{
"name": "RSI",
"studies": 3,
"children": [
{
"name": "Emergency Evacuation (3)"
}
]
}
]
},
{
"name": "VR",
"studies": 95, // Studies count for VR
"children": [
{
"name": "BEW",
"children": [
{
"name": "Route Choice & Landmark Use (9)"
},
{
"name": "Accessibility Features (6)"
},
{
"name": "Obstacle Avoidance & Terrain (4)"
},
{
"name": "Wayfinding for Special Populations (3)"
}
]
},
{
"name": "PCB",
"children": [
{
"name": "Gap Acceptance & Timing (18)"
},
{
"name": "Crosswalk Use & Compliance (8)"
},
{
"name": "Speed/Distance Perception (7)"
},
{
"name": "Trajectory & Movement Patterns (5)"
},
{
"name": "Environmental Simulation Impact (3)"
}
]
},
{
"name": "PIB",
"children": [
{
"name": "Automated Vehicle Interaction (16)"
},
{
"name": "Eye Contact & Social Cues (6"
},
{
"name": "Conflict or Negotiation Scenarios (5)"
},
{
"name": "Group Behavior (5)"
}
]
},
{
"name": "RSI",
"children": [
{
"name": "Distraction and Cognitive Load (11) "
},
{
"name": "Emergency Evacuation (7)"
},
{
"name": "Training and Safety Interventions (5)"
}
]
}
]
}
]
}

// Create a radial tree layout. The layout’s first dimension (x)
// is the angle, while the second (y) is the radius.
const tree = d3.tree()
.size([2 * Math.PI, radius])
.separation((a, b) => (a.parent == b.parent ? 1 : 2) / a.depth);

// Sort the tree and apply the layout.
const root = tree(d3.hierarchy(data)
.sort((a, b) => d3.ascending(a.data.name, b.data.name)));

// Creates the SVG container.
const svg = d3.create("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [-cx, -cy, width, height])
.attr("style", "width: 100%; height: auto; font: 10px sans-serif;");

// Append links.
svg.append("g")
.attr("fill", "none")
.attr("stroke", "#555")
.attr("stroke-opacity", 0.4)
.attr("stroke-width", 1.5)
.selectAll()
.data(root.links())
.join("path")
.attr("d", d3.linkRadial()
.angle(d => d.x)
.radius(d => d.y));

// Append nodes.
svg.append("g")
.selectAll()
.data(root.descendants())
.join("circle")
.attr("transform", d => `rotate(${d.x * 180 / Math.PI - 90}) translate(${d.y},0)`)
.attr("fill", d => d.children ? "#555" : "#999")
.attr("r", 2.5);

// Append labels.
svg.append("g")
.attr("stroke-linejoin", "round")
.attr("stroke-width", 3)
.selectAll()
.data(root.descendants())
.join("text")
.attr("transform", d => `rotate(${d.x * 180 / Math.PI - 90}) translate(${d.y},0) rotate(${d.x >= Math.PI ? 180 : 0})`)
// Restore original x and text-anchor for all labels
.attr("x", d => d.x < Math.PI === !d.children ? 6 : -6)
.attr("text-anchor", d => d.x < Math.PI === !d.children ? "start" : "end")
.attr("paint-order", "stroke")
.attr("stroke", "white")
.attr("fill", "currentColor")
.each(function(d) {
// Select the current text element
const textElement = d3.select(this);

// Clear existing text to re-render with tspans
textElement.text("");

// Append the main label (node name)
textElement.append("tspan")
.text(d.data.name)
// Apply centering to the main AR/MR/VR nodes, otherwise use default
.attr("x", d.data.studies !== undefined ? 0 : (d.x < Math.PI === !d.children ? 6 : -6))
.attr("text-anchor", d.data.studies !== undefined ? "middle" : (d.x < Math.PI === !d.children ? "start" : "end"));

// Append the studies count on a new line, only if studies data exists
if (d.data.studies !== undefined) {
textElement.append("tspan")
.attr("x", 0) // Always center the studies count relative to the node's origin
.attr("dy", "1.25em") // Offset for new line
.attr("text-anchor", "middle") // Ensure it's centered
.text(`(${d.data.studies} studies)`);
}
});

return svg.node();
}
Insert cell
data = FileAttachment("flare-2.json").json()
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