Published
Edited
Nov 19, 2020
Importers
Insert cell
Insert cell
Insert cell
Insert cell
toolCategories
Insert cell
Insert cell
categorizedTools
Insert cell
relationships = {
const relationships = new Map();
const rootCategory = d3.hierarchy(toolCategories);
const categorySlugs = categorizedTools.map(d => d.categorySlug);
const uniqueCategorySlugs = [...new Set(categorySlugs)];
for (const categorySlug of uniqueCategorySlugs) {
const node = rootCategory.find(d => d.data.slug === categorySlug);
for (const [child, parent] of d3.pairs(node.ancestors())) {
relationships.set(child.data.name, parent.data.name); // overwriting the parent (non-leaf) nodes
}
}
relationships.set(rootCategory.data.name, null); // adding the root node
return [...relationships.entries()];
}
Insert cell
stratifiedHierarchy = d3.stratify()
.id(d => d[0])
.parentId(d => d[1])
(relationships)
Insert cell
chart(stratifiedHierarchy)
Insert cell
Insert cell
{
const collapsedData = collapseSingleChildParents(stratifiedHierarchy);
const root = d3.hierarchy(collapsedData);
return chart(root);
}
Insert cell
collapseSingleChildParents = (root) => {
const copyDeep = (sourceNode) => {
const node = {
name: getText(sourceNode)
};
if (sourceNode.children) {
if (sourceNode.children.length > 1) {
node.children = [];
for (const sourceChild of sourceNode.children) {
const child = copyDeep(sourceChild);
node.children.push(child);
}
} else {
const onlyChild = sourceNode.children[0];
return copyDeep(onlyChild);
}
}
return node;
}
return copyDeep(root);
}
Insert cell
chart = (root) => {
const nodeWidth = (width / (root.height + 1)) - 10;
const nodeHeight = 10;
const layOut = d3.tree()
.nodeSize([nodeHeight, nodeWidth]); // swapping [width, height] for the horizontal layout
layOut(root);
const nodes = root.descendants();
const minNodeY = d3.min(nodes, d => d.x);
const maxNodeY = d3.max(nodes, d => d.x);
const height = (maxNodeY - minNodeY) + (nodeHeight * 2);
const svg = d3.create("svg")
.attr("class", "partial-tree")
.attr("width", width)
.attr("height", height)

const rootX = nodeWidth / 3;
const rootY = nodeHeight + Math.abs(minNodeY);
const plane = svg.append("g")
.attr("class", "plane")
.attr("transform", `translate(${rootX},${rootY})`);
appendLinks(plane, root.links());
appendNodes(plane, nodes);
return svg.node();
}
Insert cell
Insert cell
Insert cell
createPath = d3.linkHorizontal()
.x(d => d.y)
.y(d => d.x);
Insert cell
appendNodes = (container, nodes) => {
const nodeContainer = container.append("g")
.attr("class", "nodes");
nodes.forEach(node => appendNode(nodeContainer, node));
}
Insert cell
appendNode = (container, node) => {
const isLeafNode = node.children === undefined;
const nodeContainer = container.append("g")
.attr("class", isLeafNode ? "leaf-node" : "inner-node")
.attr("transform", `translate(${node.y},${node.x})`);

nodeContainer.append("circle");
appendLabel(nodeContainer, node, isLeafNode);
}
Insert cell
appendLabel = (container, node, isLeafNode) => {
const textAnchor = isLeafNode ? "start" : "end";
const dx = isLeafNode ? 6 : -6;
const dy = 3;

const labelContainer = container.append("g")
.attr("class", "label")
.attr("text-anchor", textAnchor)
.attr("transform", `translate(${dx},${dy})`);

const text = getText(node);

labelContainer.append("text")
.attr("class", "background")
.text(text);

labelContainer.append("text")
.text(text);
};
Insert cell
// stratified hierarchies have node.id instead of node.data.name (or node.data.id) set
getText = (node) => node.id ?? node.data.name
Insert cell
d3 = require("d3@6")
Insert cell
html`<style>
.links path {
fill: none;
stroke: #555;
stroke-opacity: 0.4;
stroke-width: 1.5;
}

.nodes circle {
r: 2.5;
}

.inner-node circle {
fill: #555;
}

.leaf-node circle {
fill: #999;
}

.nodes text {
font-family: sans-serif;
font-size: 10px;
}

.nodes text.background {
stroke: white;
stroke-linejoin: round;
stroke-width: 3;
}
</style>`
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