How to draw Network Topology graph using D3.js?

How to draw Network Topology graph using D3.js?

During Network related web application development some time we required to draw Topology view. Recently I worked for the Customer Vodafone to design their mobile tracking system. Here I got an opportunity to create node & traffic related topology view. To achieve this in the app I used D3.js. D3.js is a popular JavaScript library to draw graphs using SVG. It accepts JSON formatted data. Due to the customer database is with MongoDB I preferred to use D3.js to draw their Network Topology.

In the below example I am not using any database to fetch the data from server. To show you how to create a Network Topology graph using D3.js here I declared some demo data in data variable. Which is in JSON format. To start with here I referred D3.js CDN link in head part of the HTML. In body part I have a div with id “topology”. Using D3.js select method I am appending SVG to the topology div. Width & Height are the parameters of the SVG. In this example not only I implemented topology view but also I added features like drag, zoom & tooltip.

topology

To run this sample application copy the following code to a Notepad file & save it as html. Then open the HTML file. Make sure before run the file you are connected to internet. For nodes images here I added imageByType. You need to update image paths refer to your images.

Network Topology graph using D3.js

<!DOCTYPE html>
<html>
<head>
<title>Network Topology graph using D3.js</title>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.min.js"></script>
<style type="text/css">
.online-color {
stroke: #01ACC6;
stroke-width: 1px;
stroke-opacity: 1.0;
}
</style>
</head>
<body>
<div id="topology"></div>
<script type="text/javascript">

var data = {
"status": 200,
"content": {
"directed": false,
"links": [
{
"status": "online",
"source": 1,
"target": 2
},
{
"status": "online",
"source": 2,
"target": 5
},
{
"status": "online",
"source": 3,
"target": 4
},
{
"status": "online",
"source": 3,
"target": 6
}
],
"multigraph": false,
"graph": [],
"nodes": [
{
"status": "online",
"forwarding_policy": "0",
"type": "switch",
"id": "00:00:00:00:00:00:00:01",
"type_of_switch": "OF"
},
{
"status": "offline",
"_id": "54af98aea1234b06bbac8d5d",
"type": "host",
"id": "10.0.0.4"
},
{
"status": "online",
"forwarding_policy": "0",
"type": "switch",
"id": "00:00:00:00:00:00:00:03",
"type_of_switch": "OF"
},
{
"status": "online",
"forwarding_policy": "0",
"type": "switch",
"id": "00:00:00:00:00:00:00:02",
"type_of_switch": "OF"
},
{
"status": "offline",
"_id": "54af98aea1234b06bbac8d5e",
"type": "host",
"id": "10.0.0.1"
},
{
"status": "offline",
"_id": "54af98aea1234b06bbac8d5f",
"type": "host",
"id": "10.0.0.3"
},
{
"status": "offline",
"_id": "54af98aea1234b06bbac8d60",
"type": "host",
"id": "10.0.0.2"
}
],
"last_seen": "1421832648.22"
},
"message": "success"
};

var width = 1013,
height = 578,
linkedByIndex = {},
node=null,
link=null,
force=null,
nodelinks = null,
sourceStatus="",
imageByType = {
"host_online" : "images/ap.png",
"switch_online" : "images/laptop.png",
"host_offline" : "images/ipad.png",
"switch_offline" : "images/ipad.png"
};

function drawNetworkTopology(data) {
force = d3.layout.force()
.nodes(data.content.nodes)
.links(data.content.links)
.linkDistance(100)
.charge(-600)
.size([width, height])
.start();

//add zoom behavior to nodes
var zoom = d3.behavior.zoom()
.scaleExtent([1, 3])
.on("zoom", zoomed);

//add drag behavior to nodes
var drag = d3.behavior.drag()
.on("dragstart", dragstarted)
.on("drag", dragged)
.on("dragend", dragended);

//create svg element using d3
var svg = d3.select("div#topology").append("svg")
.attr("viewBox", "0 0 " + width + " " + height )
.attr("preserveAspectRatio", "xMidYMid meet")
.call(zoom);

//append container lable to bounding box
svg.append("text")
.text("")
.attr({
'x' : width-70,
'y' : height-5,
"text-anchor" : "middle",
});

//add bounding box
svg.append("rect")
.attr({
"width" : width,
"height" : height,
})
.style({
"fill" : "none",
});

//add container
var containerGrp = svg.append("g");

//add group of all lines
link = containerGrp
.selectAll("line")
.data(data.content.links)
.enter().append("line")
.attr({
"class" : function (data) {
return data.status+"-color";
}
});

//add group of all nodes
node = containerGrp
.selectAll(".node")
.data(data.content.nodes)
.enter().append("g")
.attr({
"class" : "nodes",
"cx" : function (d) {
return d.x;
},
"cy" : function (d) {
return d.y;
},
})
.call(drag);

//add image to node dynamically
node.append("image")
.attr({
"xlink:href" : function (d) {
if(d.status === ('online').toLocaleLowerCase() && d.forwarding_policy == 1)
return "images/proactive_forward.png";
else
return imageByType[d.type+"_"+d.status];
},
"x" : -15,
"y" : -15,
"width" : 30,
"height" : 30,
})
.on("click",function(d){
if (d3.event.defaultPrevented) return; // click suppressed
})
.on("mouseover", mouseOverFunction)
.on("mouseout", mouseOutFunction);
//add labeled text to each node
node.append("text")
.attr({
"y" : 25,
"text-anchor" : "middle"
})
.text(function (d) {
return d.id;
});

//tick event of network node
force
.on("tick", tick);

//map of all connected nodes index
data.content.links.forEach(function (d) {
linkedByIndex[d.source.index + "," + d.target.index] = true;
});

var tooltip = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
/**
* Event - mouseover for network nodes
* @param data
*/
function mouseOverFunction(d, i) {
if (d3.event.defaultPrevented) return;
tooltip.transition().duration(200).style("opacity", 1);
tooltip.html("<B>Properties</B><BR/><b>ID:</b>" + data.content.nodes[i].id + "<BR/><b>Type:</b>" + data.content.nodes[i].type+"<BR/><b>Status:</b>" + data.content.nodes[i].status)
.style({
"left" : (d3.event.pageX - 130) + "px",
"top" : (d3.event.pageY + 10) + "px"
});
}

/**
* Event-mouseout for network nodes
*/
function mouseOutFunction() {
if (d3.event.defaultPrevented) return;
tooltip.transition().duration(500).style("opacity", 0);
}
/**
* check for nodes connection
* @param a
* @param b
* @returns {Boolean}
*/
function isConnected(a, b) {
return isConnectedAsTarget(a, b) || isConnectedAsSource(a, b) || a.index == b.index;
}

/**
* check for node connection as soure
* @param a
* @param b
* @returns{boolean}
*/
function isConnectedAsSource(a, b) {
return linkedByIndex[a.index + "," + b.index];
}

/**
* check for node connection as target
* @param a
* @param b
* @returns {boolean}
*/
function isConnectedAsTarget(a, b) {
return linkedByIndex[b.index + "," + a.index];
}

/**
* This method can be used in conjunction with force.start() and force.stop() to compute a static layout.
*/
function tick() {
node
.attr({
"cx" : function (d) {
return d.x = Math.max(15, Math.min(width - 15, d.x));
},
"cy" : function (d) {
return d.y = Math.max(15, Math.min(height - 15, d.y));
},
"transform" : function (d) {
return "translate(" + d.x + "," + d.y + ")";
}
});
link
.attr({
"x1" : function (d) {
return d.source.x;
},
"y1" : function (d) {
return d.source.y;
},
"x2" : function (d) {
return d.target.x;
},
"y2" : function (d) {
return d.target.y;
},
});
}

/**
* zoomed function
*/
function zoomed() {
var e = d3.event,
tx = Math.min(0, Math.max(e.translate[0], width - width * e.scale)),
ty = Math.min(0, Math.max(e.translate[1], height - height * e.scale));
zoom.translate([tx, ty]);
containerGrp.attr("transform", ["translate(" + [tx, ty] + ")", "scale(" + e.scale + ")"].join(" "));
}

<span style="color:brown"><code>function dragstarted(d, i) {
force.stop(); // stops the force auto positioning before you start dragging
}

function dragged(d, i) {
d.px += d3.event.dx;
d.py += d3.event.dy;
d.x += d3.event.dx;
d.y += d3.event.dy;
tick(); // this is the key to make it work together with updating both px,py,x,y on d !
}

function dragended(d, i) {
d.fixed = true; // of course set the node to fixed so the force doesn't include the node in its auto positioning stuff
tick();
force.resume();
}
};// drawNetworkTopology() closed

drawNetworkTopology(data);

<span style="color:brown">
</body>
</html>