|
<head> |
|
<style> |
|
body { |
|
font-family: Sans-serif; |
|
margin: 0; |
|
} |
|
</style> |
|
|
|
<script src="//unpkg.com/d3-dsv"></script> |
|
<script src="//unpkg.com/d3-fetch"></script> |
|
<script src="//unpkg.com/3d-force-graph@1"></script> |
|
<script src="//unpkg.com/d3-octree"></script> |
|
<script src="//unpkg.com/d3-force-3d"></script> |
|
</head> |
|
|
|
<body> |
|
<div id="3d-graph"></div> |
|
<script> |
|
|
|
const LEVELS_GAP = 220; |
|
const HEIGHT_OFFSET = 400; |
|
const NODE_REL_SIZE = 50; |
|
|
|
const nodes = [] |
|
const links = [] |
|
|
|
d3.csv('nodes.csv').then(data => { |
|
data.forEach((level, ipaddress, mass, communityid) => { |
|
const node = { |
|
level, |
|
ipaddress, |
|
communityid, |
|
size: +mass || 20, |
|
fy: HEIGHT_OFFSET - level * LEVELS_GAP |
|
}; |
|
nodes.push(node); |
|
}); |
|
}); |
|
|
|
d3.csv('links.csv').then(data => { |
|
data.forEach((sourceIp, targetIp) => { |
|
// Get the source and target nodes |
|
var sourceNode = data.nodes.filter(function (n) { return n.IpAddress === data.sourceIp; })[0], |
|
targetNode = data.nodes.filter(function (n) { return n.IpAddress === data.targetIp; })[0]; |
|
|
|
// Add the edge to the array |
|
links.push({ source: sourceNode, target: targetNode }); |
|
}); |
|
}); |
|
|
|
const elem = document.getElementById('3d-graph'); |
|
|
|
ForceGraph3D() |
|
.nodeRelSize(NODE_REL_SIZE) |
|
.nodeId('ipAddress') |
|
.nodeVal('size') |
|
.nodeLabel('ipAddress') |
|
.nodeAutoColorBy('communityid') |
|
.nodeOpacity(0.9) |
|
.linkColor(d => d.targetNode.color) |
|
.onNodeHover(node => elem.style.cursor = node ? 'pointer' : null) |
|
.d3Force('collision', d3.forceCollide(node => Math.cbrt(node.size) * NODE_REL_SIZE)) |
|
.graphData({ nodes: nodes, links: links }) |
|
(elem); |
|
|
|
|
|
</script> |
|
</body> |