Last active
March 19, 2021 10:33
-
-
Save cydal/d3c1ca9bf1366e5cdac578950ad2fc84 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<meta charset="utf-8"> | |
<title>Research Papers Graphing Network</title> | |
<script src="//d3js.org/d3.v4.min.js"></script> | |
<style> | |
text { | |
font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif; | |
} | |
svg { | |
width: 100%; | |
} | |
.link { | |
fill: none; | |
stroke: #ddd; | |
stroke-width: 1px; | |
} | |
.node { | |
stroke: #000; | |
stroke-width: 1px; | |
} | |
</style> | |
<svg width="960" height="600"></svg> | |
<script> | |
// dimensions | |
var width = 1200; | |
var height = 1000; | |
var margin = { | |
top: 50, | |
bottom: 50, | |
left: 50, | |
right: 50, | |
} | |
// create svg | |
var svg = d3.select("body") | |
.append("svg") | |
.attr("width", width) | |
.attr("height", height) | |
.append('g') | |
.attr('transform', 'translate(' + margin.top + ',' + margin.left + ')'); | |
width = width - margin.left - margin.right; | |
height = height - margin.top - margin.bottom; | |
var legend = ["Insititutional, Political(legislative) & Social Discourse", | |
"Online Child Protection - Vulnerabilities", | |
"Technological Perspective", | |
"Commercial Perspective & Trafficking", | |
"Analysis of Offenders" | |
]; | |
var threshold = 0.38; | |
var simulation = d3.forceSimulation() | |
.force("link", d3.forceLink().id(function (d) { | |
return d.id; | |
})) | |
.force("charge", d3.forceManyBody().strength(-200)) | |
.force("collide", d3.forceCollide().radius(3)) | |
.force("center", d3.forceCenter(width / 2, height / 2)); | |
// load the graph | |
d3.json("nodes_links.json", function (error, graph) { | |
var nodes = graph.nodes; | |
function myfunc(link) { | |
if (link.value > threshold) { | |
return link | |
} | |
} | |
// links between nodes | |
var links = graph.links.map(myfunc).filter(x => x !== undefined); | |
var titles = graph.titles; | |
var color = d3.scaleOrdinal().domain([0, 1, 2, 3, 4, 5]) | |
.range(["#440154", "#3b528b", "#21918c", "#5ec962", "#fde725"]); | |
var scaleStroke = d3.scaleLinear().domain([0, 1]).range([0, 10]); | |
// Legend | |
svg.selectAll("mydots") | |
.data([0, 1, 2, 3, 4]) | |
.enter() | |
.append("circle") | |
.attr("cx", 200) | |
.attr("cy", function (d, i) { | |
return 30 + i * 35 | |
}) | |
.attr("r", 15) | |
.style("fill", function (d) { | |
return color(d); | |
}); | |
// Legend - Labels | |
svg.selectAll("mylabels") | |
.data(legend) | |
.enter() | |
.append("text") | |
.attr("x", 220) | |
.attr("y", function (d, i) { | |
return 30 + i * 35 | |
}) | |
.style("fill", function (d, i) { return color(i) }) | |
.text(function (d) { return d; }) | |
.attr("text-anchor", "left") | |
.style("alignment-baseline", "middle"); | |
// curved links | |
var link = svg.selectAll(".link") | |
.data(links) | |
.enter() | |
.append("path") | |
.attr("class", "link") | |
.attr('stroke', function (d) { | |
return "#ddd"; | |
}) | |
.attr("stroke-width", function (d) { | |
return scaleStroke(d.value); | |
}); | |
var node = svg.selectAll(".node") | |
.data(nodes) | |
.enter().append("g") | |
node.append("circle") | |
.attr("class", "node") | |
.attr("r", 8) | |
.attr("fill", function (d) { | |
return color(d.group); | |
}) | |
.on("mouseover", mouseOver(.2)) | |
.on("mouseout", mouseOut) | |
.call(d3.drag() | |
.on("start", dragstarted) | |
.on("drag", dragged) | |
.on("end", dragended));; | |
// hover text for the node | |
node.append("title") | |
.text(function (d) { | |
return titles[d.id]; | |
}); | |
node.append("text") | |
.attr("dx", 12) | |
.attr("dy", ".35em") | |
.attr("visibility", "hidden") | |
.text(function (d) { | |
return titles[d.id]; | |
}) | |
.style("stroke", "black") | |
.style("stroke-width", 0.5) | |
.style("fill", function (d) { | |
return color(d.group); | |
}); | |
simulation | |
.nodes(nodes) | |
.on("tick", ticked); | |
simulation | |
.force("link") | |
.links(links); | |
function ticked() { | |
link.attr("d", positionLink); | |
node.attr("transform", positionNode); | |
} | |
function positionLink(d) { | |
var offset = 30; | |
var midpoint_x = (d.source.x + d.target.x) / 2; | |
var midpoint_y = (d.source.y + d.target.y) / 2; | |
var dx = (d.target.x - d.source.x); | |
var dy = (d.target.y - d.source.y); | |
var normalise = Math.sqrt((dx * dx) + (dy * dy)); | |
var offSetX = midpoint_x + offset * (dy / normalise); | |
var offSetY = midpoint_y - offset * (dx / normalise); | |
return "M" + d.source.x + "," + d.source.y + | |
"S" + offSetX + "," + offSetY + | |
" " + d.target.x + "," + d.target.y; | |
} | |
// nodes within the boundaries of the svg | |
function positionNode(d) { | |
if (d.x < 0) { | |
d.x = 0 | |
}; | |
if (d.y < 0) { | |
d.y = 0 | |
}; | |
if (d.x > width) { | |
d.x = width | |
}; | |
if (d.y > height) { | |
d.y = height | |
}; | |
return "translate(" + d.x + "," + d.y + ")"; | |
} | |
// build a dictionary of nodes that are linked | |
var linkedByIndex = {}; | |
links.forEach(function (d) { | |
linkedByIndex[d.source.index + "," + d.target.index] = 1; | |
}); | |
// check the dictionary to see if nodes are linked | |
function isConnected(a, b) { | |
return linkedByIndex[a.index + "," + b.index] || linkedByIndex[b.index + "," + a.index] || a.index == b.index; | |
} | |
function mouseOver(opacity) { | |
return function (d) { | |
// check all other nodes to see if they're connected | |
// to this one. if so, keep the opacity at 1, otherwise | |
// fade | |
node.style("stroke-opacity", function (o) { | |
thisOpacity = isConnected(d, o) ? 1 : opacity; | |
return thisOpacity; | |
}); | |
node.style("fill-opacity", function (o) { | |
thisOpacity = isConnected(d, o) ? 1 : opacity; | |
return thisOpacity; | |
}); | |
node.selectAll("text") | |
.style("visibility", function(o) { | |
//debugger; | |
thisVisibility = isConnected(d, o) ? "visible" : "hidden"; | |
return thisVisibility; | |
}); | |
link.style("stroke-opacity", function (o) { | |
//debugger; | |
return o.source === d || o.target === d ? 1 : opacity; | |
}); | |
link.style("stroke", function (o) { | |
if (o.source === d || o.target === d) { | |
return color(o.source.group); | |
} | |
else { | |
return "#ddd" | |
} | |
}); | |
}; | |
} | |
function mouseOut() { | |
node.style("stroke-opacity", 1); | |
node.style("fill-opacity", 1); | |
link.style("stroke-opacity", 1); | |
link.style("stroke", "#ddd"); | |
} | |
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 dragended(d) { | |
if (!d3.event.active) simulation.alphaTarget(0); | |
d.fx = null; | |
d.fy = null; | |
} | |
}); | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment