|
<html> |
|
<head> |
|
<title>Delete nodes and edges</title> |
|
<meta charset="utf-8" /> |
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script> |
|
</head> |
|
<style> |
|
svg { |
|
height: 500px; |
|
width: 500px; |
|
border: 1px solid gray; |
|
} |
|
</style> |
|
<body> |
|
|
|
<div id="viz"> |
|
<svg> |
|
</svg> |
|
</div> |
|
</body> |
|
<footer> |
|
<script> |
|
|
|
d3.csv("firm.csv",function(error,data) {createNetwork(data)}); |
|
|
|
function createNetwork(edgelist) { |
|
var nodeHash = {}; |
|
var nodes = []; |
|
var edges = []; |
|
|
|
edgelist.forEach(function (edge) { |
|
if (!nodeHash[edge.source]) { |
|
nodeHash[edge.source] = {id: edge.source, label: edge.source}; |
|
nodes.push(nodeHash[edge.source]); |
|
} |
|
if (!nodeHash[edge.target]) { |
|
nodeHash[edge.target] = {id: edge.target, label: edge.target}; |
|
nodes.push(nodeHash[edge.target]); |
|
} |
|
if (edge.weight >= 5) { |
|
edges.push({id: nodeHash[edge.source].id + "-" + nodeHash[edge.target].id, source: nodeHash[edge.source], target: nodeHash[edge.target], weight: edge.weight}); |
|
} |
|
}); |
|
createForceNetwork(nodes, edges); |
|
} |
|
|
|
function createForceNetwork(nodes, edges) { |
|
|
|
//create a network from an edgelist |
|
|
|
//Highlight edges, delete nodes and edges |
|
|
|
var force = d3.layout.force().nodes(nodes).links(edges) |
|
.size([500,500]) |
|
.charge(function (d) {return Math.min(-100, d.weight * -50)}) |
|
.on("tick", updateNetwork); |
|
|
|
var edgeEnter = d3.select("svg").selectAll("g.edge") |
|
.data(edges, function (d) {return d.id}) |
|
.enter() |
|
.append("g") |
|
.attr("class", "edge"); |
|
|
|
edgeEnter |
|
.append("line") |
|
.attr("class", "highlight") |
|
.style("stroke-width", "8px") |
|
.style("stroke", "#66CCCC") |
|
.style("opacity", 0) |
|
.on("dblclick", deleteEdge) |
|
.on("mouseover", edgeOver) |
|
.on("mouseout", edgeOut); |
|
|
|
edgeEnter |
|
.append("line") |
|
.style("stroke-width", "1px") |
|
.style("stroke", "black") |
|
.style("pointer-events", "none"); |
|
|
|
var nodeEnter = d3.select("svg").selectAll("g.node") |
|
.data(nodes, function (d) {return d.id}) |
|
.enter() |
|
.append("g") |
|
.attr("class", "node") |
|
.on("dblclick", deleteNode) |
|
.on("mouseover", nodeOver) |
|
.on("mouseout", nodeOut) |
|
.call(force.drag()); |
|
|
|
nodeEnter.append("circle") |
|
.attr("r", 5) |
|
.style("fill", "#CC9999") |
|
.style("stroke", "black") |
|
.style("stroke-width", "1px") |
|
|
|
nodeEnter.append("text") |
|
.style("text-anchor", "middle") |
|
.attr("y", 2) |
|
.style("stroke-width", "1px") |
|
.style("stroke-opacity", 0.75) |
|
.style("stroke", "white") |
|
.style("font-size", "8px") |
|
.text(function (d) {return d.id}) |
|
.style("pointer-events", "none") |
|
|
|
nodeEnter.append("text") |
|
.style("text-anchor", "middle") |
|
.attr("y", 2) |
|
.style("font-size", "8px") |
|
.text(function (d) {return d.id}) |
|
.style("pointer-events", "none") |
|
|
|
force.start(); |
|
|
|
function nodeOver() { |
|
d3.select(this).select("circle") |
|
.style("fill", "#66CCCC") |
|
.style("stroke", "#66CCCC") |
|
.style("stroke-width", "3px"); |
|
} |
|
|
|
function nodeOut() { |
|
d3.selectAll("circle") |
|
.style("fill", "#CC9999") |
|
.style("stroke", "black") |
|
.style("stroke-width", "1px"); |
|
} |
|
|
|
function deleteNode(d) { |
|
var currentNodes = d3.selectAll("g.node").data(); |
|
var currentEdges = d3.selectAll("g.edge").data(); |
|
var filteredNodes = currentNodes.filter(function (p) {return p !== d}); |
|
var filteredEdges = currentEdges.filter(function (p) {return p.source !== d && p.target !== d}); |
|
|
|
force.stop(); |
|
force.nodes(filteredNodes); |
|
force.links(filteredEdges); |
|
|
|
d3.selectAll("g.node").data(filteredNodes, function (d) {return d.id}) |
|
.exit() |
|
.transition() |
|
.duration(500) |
|
.style("opacity", 0) |
|
.remove(); |
|
|
|
d3.selectAll("g.edge").data(filteredEdges, function (d) {return d.id}) |
|
.exit() |
|
.transition() |
|
.duration(500) |
|
.style("opacity", 0) |
|
.remove(); |
|
|
|
force.start(); |
|
} |
|
|
|
function deleteEdge(d) { |
|
var currentEdges = d3.selectAll("g.edge").data(); |
|
var filteredEdges = currentEdges.filter(function (p) {return p !== d}); |
|
|
|
force.stop(); |
|
force.links(filteredEdges); |
|
|
|
d3.selectAll("g.edge").data(filteredEdges, function (d) {return d.id}) |
|
.exit() |
|
.transition() |
|
.duration(500) |
|
.style("opacity", 0) |
|
.remove(); |
|
|
|
force.start(); |
|
} |
|
|
|
function edgeOver(d) { |
|
d3.select(this).style("opacity", 0.75); |
|
} |
|
|
|
function edgeOut() { |
|
d3.selectAll("line.highlight").style("opacity", 0); |
|
} |
|
|
|
function updateNetwork() { |
|
d3.select("svg").selectAll("line") |
|
.attr("x1", function (d) {return d.source.x}) |
|
.attr("y1", function (d) {return d.source.y}) |
|
.attr("x2", function (d) {return d.target.x}) |
|
.attr("y2", function (d) {return d.target.y}); |
|
|
|
d3.select("svg").selectAll("g.node") |
|
.attr("transform", function (d) {return "translate(" + d.x + "," + d.y + ")"}); |
|
|
|
d3.select("svg").selectAll("g.node > circle") |
|
.attr("r", function (d) {return Math.max(3, d.weight)}); |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
</script> |
|
</footer> |
|
|
|
</html> |