Reputation: 741
I am using D3
library which is new to me, with D3 visualizing a directed graph based on the graph details. As I have huge data, it's difficult to picture everything on a single page.
I am trying to filter the node with its inward & outwards dependencies by adding a dropdown option but I am stuck, not sure how to proceed.
Below is something I am trying to achieve. For example, Beta node is dependent on gamma but likewise Alpha and Delta are dependent on Beta. The below graph focuses more on Beta node and its inwards & outward dependencies.
<!DOCTYPE html>
<html>
<head>
<style type="text/css">
.node {}
.link {
stroke: #999;
stroke-opacity: .6;
stroke-width: 1px;
}
svg {
border: 1px solid black
}
</style>
</head>
<body>
<script src="https://d3js.org/d3.v4.min.js" type="text/javascript"></script>
<script src="https://d3js.org/d3-selection-multi.v1.js"></script>
<select name="cars" id="cars">
<option value="volvo">All</option>
<option value="volvo">Alpha</option>
<option value="saab">Beta</option>
<option value="volvo">Charlie</option>
<option value="saab">Delta</option>
<option value="audi">Echo</option>
<option value="opel">Foxtrot</option>
<option value="audi">Gamma</option>
<option value="opel">Hotel</option>
</select>
<br><br>
<svg width="400" height="200"></svg>
<script>
var colors = d3.scaleOrdinal(d3.schemeCategory10);
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
node,
link;
svg.append('defs').append('marker')
.attrs({
'id': 'arrowhead',
'viewBox': '-0 -5 10 10',
'refX': 13,
'refY': 0,
'orient': 'auto',
'markerWidth': 13,
'markerHeight': 13,
'xoverflow': 'visible'
})
.append('svg:path')
.attr('d', 'M 0,-5 L 10 ,0 L 0,5')
.attr('fill', '#999')
.style('stroke', 'none');
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) {
return d.id;
}).distance(100).strength(1))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2));
var graph = {
"nodes": [{
"name": "Alpha",
"label": "Person",
"id": 1
},
{
"name": "Beta",
"label": "Person",
"id": 2
},
{
"name": "Gamma",
"label": "Database",
"id": 3
},
{
"name": "Delta",
"label": "Database",
"id": 4
},
{
"name": "Echo",
"label": "Person",
"id": 5
},
{
"name": "Foxtrot",
"label": "Person",
"id": 6
},
{
"name": "Golf",
"label": "Database",
"id": 7
},
{
"name": "Hotel",
"label": "Database",
"id": 8
}
],
"links": [{
"source": 1,
"target": 2,
"type": "KNOWS",
"since": 2010
},
{
"source": 1,
"target": 3,
"type": "FOUNDED"
},
{
"source": 2,
"target": 3,
"type": "WORKS_ON"
},
{
"source": 3,
"target": 4,
"type": "IS_A"
},
{
"source": 4,
"target": 2,
"type": "KNOWS",
"since": 2010
},
{
"source": 5,
"target": 6,
"type": "FOUNDED"
},
{
"source": 3,
"target": 4,
"type": "WORKS_ON"
},
{
"source": 1,
"target": 4,
"type": "IS_A"
}
]
};
update(graph.links, graph.nodes);
function update(links, nodes) {
link = svg.selectAll(".link")
.data(links)
.enter()
.append("line")
.attr("class", "link")
.attr('marker-end', 'url(#arrowhead)')
link.append("title")
.text(function(d) {
return d.type;
});
edgepaths = svg.selectAll(".edgepath")
.data(links)
.enter()
.append('path')
.attrs({
'class': 'edgepath',
'fill-opacity': 0,
'stroke-opacity': 0,
'id': function(d, i) {
return 'edgepath' + i
}
})
.style("pointer-events", "none");
edgelabels = svg.selectAll(".edgelabel")
.data(links)
.enter()
.append('text')
.style("pointer-events", "none")
.attrs({
'class': 'edgelabel',
'id': function(d, i) {
return 'edgelabel' + i
},
'font-size': 10,
'fill': '#aaa'
});
edgelabels.append('textPath')
.attr('xlink:href', function(d, i) {
return '#edgepath' + i
})
.style("text-anchor", "middle")
.style("pointer-events", "none")
.attr("startOffset", "50%")
.text(function(d) {
return d.type
});
node = svg.selectAll(".node")
.data(nodes)
.enter()
.append("g")
.attr("class", "node")
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
//.on("end", dragended)
);
node.append("circle")
.attr("r", 5)
.style("fill", function(d, i) {
return colors(i);
})
node.append("title")
.text(function(d) {
return d.id;
});
node.append("text")
.attr("dy", -3)
.text(function(d) {
return d.name;
});
simulation.nodes(nodes)
.on("tick", ticked);
simulation.force("link")
.links(links);
}
function ticked() {
link
.attr("x1", function(d) {
checkBounds(d.source);
return d.source.x;
})
.attr("y1", function(d) {
return d.source.y;
})
.attr("x2", function(d) {
checkBounds(d.target);
return d.target.x;
})
.attr("y2", function(d) {
return d.target.y;
});
node
.attr("transform", function(d) {
checkBounds(d);
return "translate(" + d.x + ", " + d.y + ")";
});
edgepaths.attr('d', function(d) {
return 'M ' + d.source.x + ' ' + d.source.y + ' L ' + d.target.x + ' ' + d.target.y;
});
edgelabels.attr('transform', function(d) {
if (d.target.x < d.source.x) {
var bbox = this.getBBox();
rx = bbox.x + bbox.width / 2;
ry = bbox.y + bbox.height / 2;
return 'rotate(180 ' + rx + ' ' + ry + ')';
} else {
return 'rotate(0)';
}
});
}
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart()
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function checkBounds(d) {
if (d.x < 0) d.x = 0;
if (d.x > (width - 20)) d.x = width - 20;
if (d.y < 0) d.y = 0;
if (d.y > (height - 20)) d.y = height - 20;
}
</script>
</body>
</html>
Upvotes: 0
Views: 435
Reputation: 4592
I made minor changes to your code because the select item value appear to be incorrect; and I make it works for Beta, you need to fix the rest of the values to get everything to work.
var graph = {
"nodes": [{
"name": "Alpha",
"label": "Person",
"id": 1
},
{
"name": "Beta",
"label": "Person",
"id": 2
},
{
"name": "Gamma",
"label": "Database",
"id": 3
},
{
"name": "Delta",
"label": "Database",
"id": 4
},
{
"name": "Echo",
"label": "Person",
"id": 5
},
{
"name": "Foxtrot",
"label": "Person",
"id": 6
},
{
"name": "Golf",
"label": "Database",
"id": 7
},
{
"name": "Hotel",
"label": "Database",
"id": 8
}
],
"links": [{
"source": 1,
"target": 2,
"type": "KNOWS",
"since": 2010
},
{
"source": 1,
"target": 3,
"type": "FOUNDED"
},
{
"source": 2,
"target": 3,
"type": "WORKS_ON"
},
{
"source": 3,
"target": 4,
"type": "IS_A"
},
{
"source": 4,
"target": 2,
"type": "KNOWS",
"since": 2010
},
{
"source": 5,
"target": 6,
"type": "FOUNDED"
},
{
"source": 3,
"target": 4,
"type": "WORKS_ON"
},
{
"source": 1,
"target": 4,
"type": "IS_A"
}
]
};
// here is what you need to filter on
document.getElementById("cars").addEventListener("change", event => {
var val = parseInt(event.target.value, 10);
var links = graph.links.filter(link => link.source.id === val || link.target.id === val);
document.getElementsByTagName("svg")[0].innerHTML = "";
if (links.length === 0) {
update([], graph.nodes.filter(n => n.id === val));
} else {
var selectedNodes = Object.keys(links.reduce((acc, curr) => {
acc[curr.source.id] = true;
acc[curr.target.id] = true;
return acc;
}, {}));
var nodes = graph.nodes.filter(n => selectedNodes.indexOf("" + n.id) !== -1);
update(links, nodes);
}
});
var colors = d3.scaleOrdinal(d3.schemeCategory10);
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
node,
link;
var simulation = null;
update(graph.links, graph.nodes);
function update(links, nodes) {
svg.append('defs').append('marker')
.attrs({
'id': 'arrowhead',
'viewBox': '-0 -5 10 10',
'refX': 13,
'refY': 0,
'orient': 'auto',
'markerWidth': 13,
'markerHeight': 13,
'xoverflow': 'visible'
})
.append('svg:path')
.attr('d', 'M 0,-5 L 10 ,0 L 0,5')
.attr('fill', '#999')
.style('stroke', 'none');
simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) {
return d.id;
}).distance(100).strength(1))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2));
link = svg.selectAll(".link")
.data(links)
.enter()
.append("line")
.attr("class", "link")
.attr('marker-end', 'url(#arrowhead)')
link.append("title")
.text(function(d) {
return d.type;
});
edgepaths = svg.selectAll(".edgepath")
.data(links)
.enter()
.append('path')
.attrs({
'class': 'edgepath',
'fill-opacity': 0,
'stroke-opacity': 0,
'id': function(d, i) {
return 'edgepath' + i
}
})
.style("pointer-events", "none");
edgelabels = svg.selectAll(".edgelabel")
.data(links)
.enter()
.append('text')
.style("pointer-events", "none")
.attrs({
'class': 'edgelabel',
'id': function(d, i) {
return 'edgelabel' + i
},
'font-size': 10,
'fill': '#aaa'
});
edgelabels.append('textPath')
.attr('xlink:href', function(d, i) {
return '#edgepath' + i
})
.style("text-anchor", "middle")
.style("pointer-events", "none")
.attr("startOffset", "50%")
.text(function(d) {
return d.type
});
node = svg.selectAll(".node")
.data(nodes)
.enter()
.append("g")
.attr("class", "node")
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
//.on("end", dragended)
);
node.append("circle")
.attr("r", 5)
.style("fill", function(d, i) {
return colors(i);
})
node.append("title")
.text(function(d) {
return d.id;
});
node.append("text")
.attr("dy", -3)
.text(function(d) {
return d.name;
});
simulation.nodes(nodes)
.on("tick", ticked);
simulation.force("link")
.links(links);
}
function ticked() {
link
.attr("x1", function(d) {
checkBounds(d.source);
return d.source.x;
})
.attr("y1", function(d) {
return d.source.y;
})
.attr("x2", function(d) {
checkBounds(d.target);
return d.target.x;
})
.attr("y2", function(d) {
return d.target.y;
});
node
.attr("transform", function(d) {
checkBounds(d);
return "translate(" + d.x + ", " + d.y + ")";
});
edgepaths.attr('d', function(d) {
return 'M ' + d.source.x + ' ' + d.source.y + ' L ' + d.target.x + ' ' + d.target.y;
});
edgelabels.attr('transform', function(d) {
if (d.target.x < d.source.x) {
var bbox = this.getBBox();
rx = bbox.x + bbox.width / 2;
ry = bbox.y + bbox.height / 2;
return 'rotate(180 ' + rx + ' ' + ry + ')';
} else {
return 'rotate(0)';
}
});
}
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart()
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function checkBounds(d) {
if (d.x < 0) d.x = 0;
if (d.x > (width - 20)) d.x = width - 20;
if (d.y < 0) d.y = 0;
if (d.y > (height - 20)) d.y = height - 20;
}
.node {}
.link {
stroke: #999;
stroke-opacity: .6;
stroke-width: 1px;
}
svg {
border: 1px solid black
}
<!DOCTYPE html>
<html>
<head>
<script src="https://d3js.org/d3.v4.min.js" type="text/javascript"></script>
<script src="https://d3js.org/d3-selection-multi.v1.js"></script>
</head>
<body>
<select name="cars" id="cars">
<option value="volvo">All</option>
<option value="volvo">Alpha</option>
<option value="2">Beta</option>
<option value="volvo">Charlie</option>
<option value="saab">Delta</option>
<option value="audi">Echo</option>
<option value="opel">Foxtrot</option>
<option value="audi">Gamma</option>
<option value="opel">Hotel</option>
</select>
<br><br>
<svg width="400" height="200"></svg>
</body>
</html>
Upvotes: 1