This demo shows call traffic inside a small network over a 12 month period.
Link widths are a good way to represent different volumes of traffic between nodes. Seeing link widths change dynamically over time gives you a clear overview of events, and highlights unusual patterns that you can drill into further.
The time bar lets you cycle through the year and easily spot who phoned who, and how often. You can zoom into the time bar histogram to focus on calls that took place at a certain time on a specific date between nodes of interest.
Key functions used:
import KeyLines from "keylines";
import data from "./data.js";
let chart;
let timebar;
const MAX_LINK_WIDTH = 10;
const resetButton = document.getElementById("reset");
const startStopButton = document.getElementById("start-stop");
function scaleLinks() {
const widthList = [];
let maxWidth = -1;
// get the current range and parse it to a timestamp
const timeRange = timebar.range();
const itemsInRange = timebar.getIds(timeRange.dt1, timeRange.dt2);
itemsInRange.forEach((id) => {
const item = chart.getItem(id);
// calculate the number of times each connection occured in the current range
if (item && item.type === "link") {
let width = 0;
item.dt.forEach((time) => {
if (time >= timeRange.dt1 && time <= timeRange.dt2) {
width += 1;
}
});
maxWidth = Math.max(maxWidth, width);
widthList.push({ id, w: width });
}
});
// scale all widths relative to widest link
widthList.forEach((item) => {
item.w = (MAX_LINK_WIDTH * item.w) / maxWidth;
});
chart.animateProperties(widthList, { time: 100 });
}
function reset() {
timebar.pause();
timebar.range(new Date(2000, 0), new Date(2000, 1), { animate: false });
}
function startStop() {
if (startStopButton.innerHTML === "Play") {
timebar.play();
} else {
timebar.pause();
}
}
async function klReady() {
// load the data (defined in separate js file) into both the chart and time bar
timebar.load(data);
reset();
chart.load(data);
await chart.filter(timebar.inRange, { animate: false, type: "link" });
scaleLinks();
chart.layout("organic");
// When the time bar range changes
timebar.on("change", async () => {
scaleLinks();
// filter the chart to show only items in the new range
await chart.filter(timebar.inRange, { animate: false, type: "link" });
chart.layout("organic", { mode: "adaptive", time: 500, fixed: ["14", "15"] });
});
timebar.on("end", timebar.pause);
timebar.on("play", () => {
startStopButton.innerHTML = "Pause";
});
timebar.on("pause", () => {
startStopButton.innerHTML = "Play";
});
resetButton.addEventListener("click", reset);
startStopButton.addEventListener("click", startStop);
}
async function loadKeyLines() {
await document.fonts.load('24px "Font Awesome 5 Free"');
const options = {
logo: "/images/Logo.png",
handMode: true,
overview: { icon: false, shown: false },
iconFontFamily: "Font Awesome 5 Free",
};
[chart, timebar] = await KeyLines.create([
{ container: "klchart", options },
{ container: "kltimebar", type: "timebar" },
]);
klReady();
}
window.addEventListener("DOMContentLoaded", loadKeyLines); import KeyLines from "keylines";
export default {
type: "LinkChart",
items: [
{
type: "node",
id: 1,
t: "(020) xxxx 2781",
fi: {
t: ".fas .fa-phone-square",
c: "#25D366",
},
},
{
id2: 14,
id1: 1,
a1: true,
dt: [
949120540713, 948854251006, 948370724603, 946722540092, 947457913508, 948587667519,
947039610840, 951312458835, 952196349612, 955586932529, 958878251442, 962053790907,
964226213512, 967133085073, 968735438922, 972042033625, 974425518500, 974783881627,
974983227932, 974556970943, 974180170083, 974431294228, 975286367877, 976725625360,
],
type: "link",
id: "1-14",
},
{
id2: 14,
id1: 1,
a2: true,
dt: [958263499173, 977220151711, 971055649757, 971324729157, 960304110368],
type: "link",
id: "1-14-2",
},
{
type: "node",
id: 2,
t: "(020) xxxx 5697",
fi: {
t: ".fas .fa-phone-square",
c: "#25D366",
},
},
{
id2: 14,
id1: 2,
a1: true,
dt: [
946830798790, 950330393027, 952127005380, 954780761820, 959377757958, 961211311508,
963677582593, 967611052898, 968392906195, 971406168424, 971521050776, 972156038035,
971369588716, 972044236062, 971832965750, 971752493456, 974148717948, 973491509704,
973688870185, 975139355792, 975360509904, 975038478227, 974848625408, 977853189901,
],
type: "link",
id: "2-14",
},
{
id2: 14,
id1: 2,
a2: true,
dt: [960275656342, 952557144649, 976846124722, 959071556936, 951181768093],
type: "link",
id: "2-14-2",
},
{
type: "node",
id: 3,
t: "(020) xxxx 0435",
fi: {
t: ".fas .fa-phone-square",
c: "#25D366",
},
},
{
id2: 14,
id1: 3,
a1: true,
dt: [
947097887182, 950063326910, 954575469917, 955109642881, 958284798440, 961783891422,
962354867065, 961850176505, 961433762510, 961651398947, 961555068503, 962297684520,
963791231209, 967723180279, 968366058924, 972688719610, 971899814770, 971438198680,
971139068337, 970932653212, 971306940516, 970874920867, 975604368287, 977518304142,
],
type: "link",
id: "3-14",
},
{
id2: 14,
id1: 3,
a2: true,
dt: [947816921816, 976806439810, 949104589999, 952280201759, 961451309789],
type: "link",
id: "3-14-2",
},
{
type: "node",
id: 4,
t: "(020) xxxx 7436",
fi: {
t: ".fas .fa-phone-square",
c: "#25D366",
},
},
{
id2: 14,
id1: 4,
a1: true,
dt: [
947296872725, 949665463708, 953115981019, 956588595780, 956672752586, 955415496335,
954949475278, 955866784937, 956057157958, 956667247892, 959649637161, 957787768957,
958081348737, 958520201463, 958333199222, 958124361666, 957995989079, 961237018972,
963411402939, 965922868236, 968518129379, 971757605569, 974991095145, 977912400283,
],
type: "link",
id: "4-14",
},
{
id2: 14,
id1: 4,
a2: true,
dt: [947246585259, 956120773473, 970973350090, 953965329250, 961896784133],
type: "link",
id: "4-14-2",
},
{
type: "node",
id: 5,
t: "(020) xxxx 8166",
fi: {
t: ".fas .fa-phone-square",
c: "#25D366",
},
},
{
id2: 14,
id1: 5,
a1: true,
dt: [
947636816499, 948036784973, 949261788009, 949086461685, 948875175611, 948198980385,
948095511961, 949374998206, 952331454689, 952105820270, 953690088016, 953642506596,
953938970095, 953204977525, 953922309487, 954996724402, 959297444056, 960216744545,
964023335974, 963562403629, 964858227982, 963079617031, 963655809815, 963420230421,
964289174244, 966243572303, 965774425654, 966491927107, 967525493608, 965713485223,
965774839359, 965590954249, 969857653694, 969576501623, 970132682790, 969399897432,
968247645720, 969473425093, 970126553144, 972899219866, 974111830684, 977787699278,
],
type: "link",
id: "5-14",
},
{
id2: 14,
id1: 5,
a2: true,
dt: [977964514447, 969015253002, 951005693001, 957225015612, 970400130371],
type: "link",
id: "5-14-2",
},
{
type: "node",
id: 6,
t: "(020) xxxx 4387",
fi: {
t: ".fas .fa-phone-square",
c: "#25D366",
},
},
{
id2: 14,
id1: 6,
a1: true,
dt: [
947744034783, 949837337834, 951030608560, 950878150636, 951127962900, 949907256078,
951802319850, 950140474097, 952809825306, 956002115940, 958680642462, 960582045754,
964440657134, 966748274877, 968205420606, 972218682697, 975401684185, 974923840500,
974862501825, 973526190255, 975528874235, 975213101630, 974454802880, 977656124028,
977673893191, 977416836407, 977391677873, 976800560911, 976253517725, 976910959605,
],
type: "link",
id: "6-14",
},
{
id2: 14,
id1: 6,
a2: true,
dt: [968491879984, 973821532901, 949698199251, 961313917602, 959890833509],
type: "link",
id: "6-14-2",
},
{
type: "node",
id: 7,
t: "(020) xxxx 2720",
fi: {
t: ".fas .fa-phone-square",
c: "#25D366",
},
},
{
id2: 14,
id1: 7,
a1: true,
dt: [
947288725973, 951560331834, 953999039852, 955177946388, 959365754864, 960464666151,
963842650926, 966637956422, 969181136730, 968947655607, 968930626911, 968705895401,
968124258748, 968282864008, 969164944156, 971170705051, 974930653132, 978076179900,
],
type: "link",
id: "7-14",
},
{
id2: 14,
id1: 7,
a2: true,
dt: [967125559989, 955347402281, 971104775072, 955195373540, 964693886781],
type: "link",
id: "7-14-2",
},
{
type: "node",
id: 8,
t: "(020) xxxx 6380",
fi: {
t: ".fas .fa-phone-square",
c: "#25D366",
},
},
{
id2: 14,
id1: 8,
a1: true,
dt: [
948536018925, 951657348969, 952064051220, 956263211415, 957963538850, 960807451536,
964979720544, 966813274135, 968521052665, 971877620848, 973505055632, 977073144733,
],
type: "link",
id: "8-14",
},
{
id2: 14,
id1: 8,
a2: true,
dt: [963412039275, 958477923763, 970169248634, 976634060860, 965923478596],
type: "link",
id: "8-14-2",
},
{
type: "node",
id: 9,
t: "(020) xxxx 6713",
fi: {
t: ".fas .fa-phone-square",
c: "#25D366",
},
},
{
id2: 15,
id1: 9,
a1: true,
dt: [
948907940658, 950616060196, 953592067745, 954742719549, 958086431277, 961604099393,
963675403792, 966365677624, 968458479961, 972351460258, 974317059347, 976918962282,
],
type: "link",
id: "9-15",
},
{
id2: 15,
id1: 9,
a2: true,
dt: [949809330586, 947434418292, 959414847956, 971640107036, 960035126581],
type: "link",
id: "9-15-2",
},
{
type: "node",
id: 10,
t: "(020) xxxx 3605",
fi: {
t: ".fas .fa-phone-square",
c: "#25D366",
},
},
{
id2: 15,
id1: 10,
a1: true,
dt: [
949112216329, 951948734486, 953932243789, 954723769506, 957654013146, 961170325541,
963076382249, 967479744486, 969844775751, 972407605212, 973723702736, 978168739526,
],
type: "link",
id: "10-15",
},
{
id2: 15,
id1: 10,
a2: true,
dt: [977071775971, 965824352856, 964417780051, 969132521302, 969219934602],
type: "link",
id: "10-15-2",
},
{
type: "node",
id: 11,
t: "(020) xxxx 6079",
fi: {
t: ".fas .fa-phone-square",
c: "#25D366",
},
},
{
id2: 15,
id1: 11,
a1: true,
dt: [
947940802059, 951542226127, 952383572184, 953585952010, 952623333930, 953634380536,
954131073083, 953095000565, 954252699095, 956288262145, 955692754818, 955470992113,
955825690414, 956201772981, 956762389340, 956210685200, 959830160343, 960691057241,
963600634958, 967731858874, 968361880257, 972350244938, 974307214341, 976598964334,
977936941482, 977765310096, 977838779079, 976760791971, 977919326375, 978275634321,
],
type: "link",
id: "11-15",
},
{
id2: 15,
id1: 11,
a2: true,
dt: [952241372815, 967606388598, 959101190452, 972666780332, 953951539308],
type: "link",
id: "11-15-2",
},
{
type: "node",
id: 12,
t: "(020) xxxx 7740",
fi: {
t: ".fas .fa-phone-square",
c: "#25D366",
},
},
{
id2: 15,
id1: 12,
a1: true,
dt: [
949098675628, 950441665010, 952046758707, 954734230631, 957981133207, 961850409745,
963316663448, 967481517424, 970217896883, 971838427320, 974364242687, 974294097647,
975560841812, 974003154305, 973943275539, 973876854021, 974978858320, 976681958185,
],
type: "link",
id: "12-15",
},
{
id2: 15,
id1: 12,
a2: true,
dt: [958708728028, 954128951712, 950622125599, 954533243472, 949126054426],
type: "link",
id: "12-15-2",
},
{
type: "node",
id: 13,
t: "(020) xxxx 9281",
fi: {
t: ".fas .fa-phone-square",
c: "#25D366",
},
},
{
id2: 15,
id1: 13,
a1: true,
dt: [
947934638447, 950504380121, 953368676836, 956418852126, 956335515136, 956358233612,
956846466610, 957041719004, 957052601071, 955871780027, 959370044634, 960841474054,
960770238063, 960265204600, 961224374154, 960632596647, 960494005431, 962196686411,
963172229730, 965433634075, 969557782241, 970924197858, 975468888668, 977023641121,
],
type: "link",
id: "13-15",
},
{
id2: 15,
id1: 13,
a2: true,
dt: [965288763627, 959574955530, 972305281526, 961123374515, 968490106738],
type: "link",
id: "13-15-2",
},
{
type: "node",
id: 14,
t: "(020) xxxx 8298",
fi: {
t: ".fas .fa-phone-square",
c: "#25D366",
},
},
{
id2: "15",
id1: "14",
a1: true,
dt: [
949083769627, 951846326556, 952925026393, 956645127225, 958198401637, 960803758925,
965027974296, 967066814063, 970387451337, 972336342026, 974393840125, 976976392019,
],
type: "link",
id: "14-15",
},
{
type: "node",
id: 15,
t: "(020) xxxx 1118",
fi: {
t: ".fas .fa-phone-square",
c: "#25D366",
},
},
{
id2: "15",
id1: "14",
a1: true,
dt: [
948157482661, 949722002524, 954385091552, 955907378200, 959317503143, 961982917014,
962938820918, 967685374623, 969871239288, 972362579909, 975401927868, 977773601740,
],
type: "link",
id: "14-15",
},
],
}; <!doctype html>
<html lang="en" style="background-color: #2d383f">
<head>
<meta charset="utf-8" />
<title>Animate Link Widths</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 klchart-timebar"></div>
<div id="kltimebar" class="kltimebar"></div>
<script type="module" src="./code.js"></script>
</body>
</html>