Many networks are hierarchical in nature. Such networks can be identified by the nature of their links, e.g. a network of company employees will indicate 'who reports to whom'. With a complex network it can be difficult to establish these relations and it's helpful to be able to automate analysis.
The chart shows a network of people and companies. People may own one or more companies, and companies may control or be controlled by other companies.
The demo code uses some simple functions to find nodes to which there is a path from the selected node, and then applies a sequential layout to the returned items.
Key functions used:
import KeyLines from "keylines";
import { chartData } from "./data.js";
// declaring global vars
let chart;
function getOrientation() {
const selection = chart.selection();
if (selection.length > 0) {
const item = chart.getItem(selection[0]);
if (item.type === "node" && item.d.type === "person") {
return "down";
}
}
return "up";
}
function findConnected(ids) {
// merge all the distances: we do not care about metrics here, but only if a node is reachable
let items = [];
const list = chart.getItem(ids);
list.forEach((item) => {
if (item.type === "node") {
const direction = item.d.type === "company" ? "to" : "from";
// merge results
items = items.concat(
Object.keys(chart.graph().distances(item.id, { direction })),
);
}
});
return items;
}
async function filterConnected(id) {
chart.lock(true);
const ids = [id, ...chart.selection()];
const items = findConnected(ids);
await chart.filter((item) => items.includes(item.id), { type: "node" });
await chart.layout("sequential", { top: ids, orientation: getOrientation() });
chart.lock(false);
}
async function restoreAndShowAll() {
chart.lock(true);
chart.selection([]);
await chart.filter(() => true, { time: 500 });
chart.layout("organic", { consistent: true, tightness: 2 });
await chart.zoom("fit", { time: 500, animate: true });
chart.lock(false);
}
function onClick({ id }) {
const item = chart.getItem(id);
if (item && item.type === "node") {
filterConnected(id);
}
}
function onDoubleclick({ id, preventDefault }) {
const item = chart.getItem(id);
if (item === null) {
restoreAndShowAll();
}
// disable zoom
preventDefault();
}
async function startKeyLines() {
const imageAlignment = {
"fas fa-user": { dy: -10 },
"fas fa-building": { dy: 2, e: 0.9 },
};
const options = {
logo: { u: "/public/images/Logo.png" },
linkEnds: { avoidLabels: false },
iconFontFamily: "Font Awesome 5 Free",
imageAlignment,
hover: 5,
handMode: true,
};
chart = await KeyLines.create({ container: "klchart", options });
chart.on("click", onClick);
chart.on("double-click", onDoubleclick);
chart.load(chartData);
await chart.zoom("fit");
chart.layout("organic", { consistent: true, tightness: 2 });
}
function loadKeyLines() {
document.fonts.load('24px "Font Awesome 5 Free"').then(startKeyLines);
}
window.addEventListener("DOMContentLoaded", loadKeyLines); import KeyLines from "keylines";
export const chartData = {
type: "LinkChart",
items: [
{
type: "node",
id: "Person 18",
t: "Person 18",
c: "#17ad56",
fi: {
t: "fas fa-user",
c: "white",
},
d: {
type: "person",
},
},
{
type: "node",
id: "Person 11",
t: "Person 11",
c: "#17ad56",
fi: {
t: "fas fa-user",
c: "white",
},
d: {
type: "person",
},
},
{
type: "node",
id: "Person 1",
t: "Person 1",
c: "#17ad56",
fi: {
t: "fas fa-user",
c: "white",
},
d: {
type: "person",
},
},
{
type: "node",
id: "Person 13",
t: "Person 13",
c: "#17ad56",
fi: {
t: "fas fa-user",
c: "white",
},
d: {
type: "person",
},
},
{
type: "node",
id: "Person 17",
t: "Person 17",
c: "#17ad56",
fi: {
t: "fas fa-user",
c: "white",
},
d: {
type: "person",
},
},
{
type: "node",
id: "Person 10",
t: "Person 10",
c: "#17ad56",
fi: {
t: "fas fa-user",
c: "white",
},
d: {
type: "person",
},
},
{
type: "node",
id: "Person 6",
t: "Person 6",
c: "#17ad56",
fi: {
t: "fas fa-user",
c: "white",
},
d: {
type: "person",
},
},
{
type: "node",
id: "Person 3",
t: "Person 3",
c: "#17ad56",
fi: {
t: "fas fa-user",
c: "white",
},
d: {
type: "person",
},
},
{
type: "node",
id: "Person 2",
t: "Person 2",
c: "#17ad56",
fi: {
t: "fas fa-user",
c: "white",
},
d: {
type: "person",
},
},
{
type: "node",
id: "Person 12",
t: "Person 12",
c: "#17ad56",
fi: {
t: "fas fa-user",
c: "white",
},
d: {
type: "person",
},
},
{
type: "node",
id: "Person 4",
t: "Person 4",
c: "#17ad56",
fi: {
t: "fas fa-user",
c: "white",
},
d: {
type: "person",
},
},
{
type: "node",
id: "Person 15",
t: "Person 15",
c: "#17ad56",
fi: {
t: "fas fa-user",
c: "white",
},
d: {
type: "person",
},
},
{
type: "node",
id: "Person 5",
t: "Person 5",
c: "#17ad56",
fi: {
t: "fas fa-user",
c: "white",
},
d: {
type: "person",
},
},
{
type: "node",
id: "Person 16",
t: "Person 16",
c: "#17ad56",
fi: {
t: "fas fa-user",
c: "white",
},
d: {
type: "person",
},
},
{
type: "node",
id: "Person 9",
t: "Person 9",
c: "#17ad56",
fi: {
t: "fas fa-user",
c: "white",
},
d: {
type: "person",
},
},
{
type: "node",
id: "Person 19",
t: "Person 19",
c: "#17ad56",
fi: {
t: "fas fa-user",
c: "white",
},
d: {
type: "person",
},
},
{
type: "node",
id: "Person 7",
t: "Person 7",
c: "#17ad56",
fi: {
t: "fas fa-user",
c: "white",
},
d: {
type: "person",
},
},
{
type: "node",
id: "Person 20",
t: "Person 20",
c: "#17ad56",
fi: {
t: "fas fa-user",
c: "white",
},
d: {
type: "person",
},
},
{
type: "node",
id: "Person 8",
t: "Person 8",
c: "#17ad56",
fi: {
t: "fas fa-user",
c: "white",
},
d: {
type: "person",
},
},
{
type: "node",
id: "Person 14",
t: "Person 14",
c: "#17ad56",
fi: {
t: "fas fa-user",
c: "white",
},
d: {
type: "person",
},
},
{
type: "node",
id: "Company 93",
t: "Company 93",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 54",
t: "Company 54",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 44",
t: "Company 44",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 2",
t: "Company 2",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 15",
t: "Company 15",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 7",
t: "Company 7",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 23",
t: "Company 23",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 18",
t: "Company 18",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 47",
t: "Company 47",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 61",
t: "Company 61",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 49",
t: "Company 49",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 89",
t: "Company 89",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 90",
t: "Company 90",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 13",
t: "Company 13",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 4",
t: "Company 4",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 17",
t: "Company 17",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 36",
t: "Company 36",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 82",
t: "Company 82",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 80",
t: "Company 80",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 20",
t: "Company 20",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 6",
t: "Company 6",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 8",
t: "Company 8",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 9",
t: "Company 9",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 59",
t: "Company 59",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 37",
t: "Company 37",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 51",
t: "Company 51",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 38",
t: "Company 38",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 28",
t: "Company 28",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 39",
t: "Company 39",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 96",
t: "Company 96",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 58",
t: "Company 58",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 86",
t: "Company 86",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 64",
t: "Company 64",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 56",
t: "Company 56",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 19",
t: "Company 19",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 14",
t: "Company 14",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 32",
t: "Company 32",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 46",
t: "Company 46",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 52",
t: "Company 52",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 67",
t: "Company 67",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 98",
t: "Company 98",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 21",
t: "Company 21",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 66",
t: "Company 66",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 42",
t: "Company 42",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 55",
t: "Company 55",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 92",
t: "Company 92",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 88",
t: "Company 88",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 99",
t: "Company 99",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 41",
t: "Company 41",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 72",
t: "Company 72",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 53",
t: "Company 53",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 5",
t: "Company 5",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 97",
t: "Company 97",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 83",
t: "Company 83",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 84",
t: "Company 84",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 87",
t: "Company 87",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
},
d: {
type: "company",
},
},
{
type: "node",
id: "Company 10",
t: "Company 10",
c: "#03b1fc",
fi: {
t: "fas fa-building",
c: "white",
// ...truncated 2667 lines <!doctype html>
<html lang="en" style="background-color: #2d383f">
<head>
<meta charset="utf-8" />
<title>Hierarchical Dependencies</title>
<link rel="stylesheet" type="text/css" href="@ci/theme/kl/css/keylines.css" />
<link rel="stylesheet" type="text/css" href="@ci/theme/kl/css/minimalsdk.css" />
<link rel="stylesheet" type="text/css" href="@ci/theme/kl/css/sdk-layout.css" />
<link rel="stylesheet" type="text/css" href="@ci/theme/kl/css/demo.css" />
<link
rel="stylesheet"
type="text/css"
href="@fortawesome/[email protected]/css/fontawesome.css"
/>
<link
rel="stylesheet"
type="text/css"
href="@fortawesome/[email protected]/css/solid.css"
/>
</head>
<body>
<div id="klchart" class="klchart"></div>
<script type="module" src="./code.js"></script>
</body>
</html>