BOM Visualization

A Bill of Materials (BOM) is structured list of components, subcomponents, and other relevant information needed to create an end product. More complex BOMs also include financial and operational data at the component level, such as supplier, cost, and manufacturing coordinates. As one can imagine, BOMs are critical for supply chains and contain highly confidential information.

BOMs are of hierarchical nature, with the top level representing the finished product and lower levels comprising of subcomponents. Most companies use an ERP system or Excel spreadsheets to map this relationship, which I find rather dull and archaic. As an attempt to create something more engaging, I developed BOMs using a data visualization API known as D3.js. The components are stored in a simple JSON file, while the API calls JavaScript files to render the structures in HTML. Check out two sample BOMs below, one for a real-world use case and the other just for fun.

 

Data Center

To protect sensitive and confidential data, I have used an obfuscated BOM for illustration purposes.

From a Google search, email, social media post, movie stream, or video call, our world’s digital infrastructure runs on data centers. I’m fortunate to have worked with Amazon and its cloud computing arm, Amazon Web Services (AWS), where I learned first-hand the importance of data centers and the immense amount of work that goes behind each one.

So what exactly is a data center? In short, it is a centralized location where computing and networking equipment is concentrated to collect, store, process, distribute or allow access to data (e.g. this website itself).

In order to build a data center though, there are many parts and processes at play. With this BOM visualization, I aim to show a high-level insight of what goes into building a data facility. Unlike traditional products, this BOM is a much bigger picture with tier-five+ subcomponents. Each part is critical to the supply chain and infrastructure of how the internet works.

Flux Capacitor

Visualization a completely hypothetical time traveling machine to illustrate the purpose of a dynamic BOM.

According to renowned Dr. Emmett Brown (completely fictional character), the flux capacitor is the core component of the time machine making time travel possible.

I somehow got ahold of this time travel machine BOM , and visualized it here for you to build yourself. Great Scott!


Code

Find the full source code and resources on https://github.com/d3/d3.

export default function define(runtime, observer) {
  const main = runtime.module();
  const fileAttachments = new Map([["flare-2.json",new URL("./files/e65374209781891f37dea1e7a6e1c5e020a3009b8aedf113b4c80942018887a1176ad4945cf14444603ff91d3da371b3b0d72419fa8d2ee0f6e815732475d5de",import.meta.url)]]);
  main.builtin("FileAttachment", runtime.fileAttachments(name => fileAttachments.get(name)));
  main.variable(observer("chart")).define("chart", ["tree","data","d3","width"], function(tree,data,d3,width)
{
  const root = tree(data);

  let x0 = Infinity;
  let x1 = -x0;
  root.each(d => {
    if (d.x > x1) x1 = d.x;
    if (d.x < x0) x0 = d.x;
  });

  const svg = d3.create("svg")
      .attr("viewBox", [0, 0, width, x1 - x0 + root.dx * 2]);
  
  const g = svg.append("g")
      .attr("font-family", "sans-serif")
      .attr("font-size", 10)
      .attr("transform", `translate(${root.dy / 3},${root.dx - x0})`);
    
  const link = g.append("g")
    .attr("fill", "none")
    .attr("stroke", "#555")
    .attr("stroke-opacity", 0.4)
    .attr("stroke-width", 1.5)
  .selectAll("path")
    .data(root.links())
    .join("path")
      .attr("d", d3.linkHorizontal()
          .x(d => d.y)
          .y(d => d.x));
  
  const node = g.append("g")
      .attr("stroke-linejoin", "round")
      .attr("stroke-width", 3)
    .selectAll("g")
    .data(root.descendants())
    .join("g")
      .attr("transform", d => `translate(${d.y},${d.x})`);

  node.append("circle")
      .attr("fill", d => d.children ? "#555" : "#999")
      .attr("r", 2.5);

  node.append("text")
      .attr("dy", "0.31em")
      .attr("x", d => d.children ? -6 : 6)
      .attr("text-anchor", d => d.children ? "end" : "start")
      .text(d => d.data.name)
    .clone(true).lower()
      .attr("stroke", "white");
  
  return svg.node();
}
);
  main.variable(observer("data")).define("data", ["FileAttachment"], function(FileAttachment){return(
FileAttachment("flare-2.json").json()
)});
  main.variable(observer("tree")).define("tree", ["d3","width"], function(d3,width){return(
data => {
  const root = d3.hierarchy(data);
  root.dx = 10;
  root.dy = width / (root.height + 1);
  return d3.tree().nodeSize([root.dx, root.dy])(root);
}
)});
  main.variable(observer("width")).define("width", function(){return(
954
)});
  main.variable(observer("d3")).define("d3", ["require"], function(require){return(
require("d3@5")
)});
  return main;
}
Previous
Previous

Space Supply Chains: The Final Mile Frontier

Next
Next

3D and AR Rendering