a look at README.md
files from bl.ocks.org that contain links to other bl.ocks
click on a node to view that bl.ock
community detection done with the jLouvain library
source data and scripts used to generate the graph data are at this repository
a lineage of bl.ocks that informed this idea:
MIT License
xxxxxxxxxx
<html>
<head>
<title>Blocks Graph - Readme Mentions</title>
<meta charset="utf-8" />
<script src="https://d3js.org/d3.v3.min.js"></script>
<script src="jsLouvain.js"></script>
<style>
/*body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }*/
/*svg { width: 100%; height: 100%; }*/
/* Make the chart container fill the page using CSS. */
#viz {
position: fixed;
left: 0px;
right: 0px;
top: 0px;
bottom: 0px;
}
</style>
</head>
<body>
<div id="viz"></div>
<script>
// Extract the width and height that was computed by CSS.
var vizDiv = document.getElementById("viz");
var width = vizDiv.clientWidth;
var height = vizDiv.clientHeight;
var minSide = d3.min([width, height]);
var xOffset = (width - minSide) / 2;
var yOffset = (height - minSide) / 2;
// Use the extracted size to set the size of an SVG element.
var svg = d3.select(vizDiv).append("svg")
.attr("width", width)
.attr("height", height)
.classed("main", true);
d3.json("readme-blocks-graph.json", function(error,data) { createNetwork(data) });
function onlyUnique(value, index, self) {
return self.indexOf(value) === index;
}
function createNetwork(graphContainer) {
var nodeHash = {};
var nodes = [];
var edges = [];
var edgelist = graphContainer["graph"]["edges"];
var nodelist = graphContainer["graph"]["nodes"];
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: 1/*edge.weight*/});
}
});
// get some attributes from the nodelist (calculated elsewhere)
// and store them in the nodeHash
nodelist.forEach(function (node) {
if(nodeHash[node.id]) {
nodeHash[node.id]["user"] = node["user"];
nodeHash[node.id]["createdAt"] = node["createdAt"];
nodeHash[node.id]["updatedAt"] = node["updatedAt"];
nodeHash[node.id]["description"] = node["description"];
}
})
// take the attributes now in the nodeHash
// and hang them on the nodes (calculated here from the edgelist)
nodes.forEach(function (node) {
if(nodeHash[node.id]) {
node["user"] = nodeHash[node.id]["user"];
node["createdAt"] = nodeHash[node.id]["createdAt"];
node["updatedAt"] = nodeHash[node.id]["updatedAt"];
node["description"] = nodeHash[node.id]["description"];
}
})
console.log("nodes", nodes);
console.log("edges", edges);
createForceNetwork(nodes, edges);
}
function modularityCensus(nodes, edges, moduleHash) {
edges.forEach(function (edge) {
if (edge.source.module !== edge.target.module) {
edge.border = true;
}
else {
edge.border = false;
}
});
nodes.forEach(function (node) {
var theseEdges = edges.filter(function(d) {return d.source === node || d.target === node});
var theseSourceModules = theseEdges.map(function (d) {return d.source.module}).filter(onlyUnique);
var theseTargetModules = theseEdges.map(function (d) {return d.target.module}).filter(onlyUnique);
if (theseSourceModules.length > 1 || theseTargetModules.length > 1) {
node.border = true;
}
else {
node.border = false;
}
});
}
function createForceNetwork(nodes, edges) {
//create a network from an edgelist
//var colors = d3.scale.ordinal().domain([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]).range(["#996666", "#66CCCC", "#FFFF99", "#CC9999", "#666633", "#993300", "#999966", "#660000", "#996699", "#cc6633", "#ff9966", "#339999", "#6699cc", "#ffcc66", "#ff6600", "#00ccccc"]);
var colors = d3.scale.category20();
var node_data = nodes.map(function (d) {return d.id});
var edge_data = edges.map(function (d) {return {source: d.source.id, target: d.target.id, weight: 1}; });
console.log("node_data for jLouvain", node_data);
console.log("edge_data for jLouvain", edge_data);
var community = jLouvain().nodes(node_data).edges(edge_data);
var result = community();
console.log("jLouvain result", result);
nodes.forEach(function (node) {
node.module = result[node.id]
//console.log("node.module", node.module)
});
modularityCensus(nodes, edges, result);
var force = d3.layout.force().nodes(nodes).links(edges)
.size([minSide, minSide]) // make a square
.charge(-100)
.chargeDistance(100)
.linkStrength(2)
.linkDistance(3)
.gravity(0.07);
var edgeEnter = d3.select("svg.main").selectAll("g.edge")
.data(edges, function (d) {return d.id})
.enter()
.append("g")
.attr("class", "edge");
edgeEnter
.append("line")
.style("stroke-width", "1px")
.style("stroke", "gray")
.style("pointer-events", "none");
var nodeEnter = d3.select("svg.main").selectAll("g.node")
.data(nodes, function (d) {return d.id})
.enter();
nodeEnter
.append("a")
.attr("xlink:href", function (d){
return "https://bl.ocks.org/" + d.user + "/" + d.id;
})
//.attr("target", "_blank")
.append("circle")
.attr("r", 3)
.attr("class", "foreground")
.style("fill", function (d) {return colors(d.module)})
.style("stroke-width", function (d) {return d.border ? "3px" : "1px"})
/*
// draw labels over nodes
nodeEnter.append("text")
.style("text-anchor", "middle")
.attr("y", 3)
.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", 3)
.style("font-size", "8px")
.text(function (d) {return d.id})
.style("pointer-events", "none")
*/
force.start();
for(var i = 0; i < 200; i++){
force.tick();
}
//force.on("tick", updateNetwork);
// after 200 iterations, we say the network
// has stabilized and we stop the
// force layout physics simulation
force.stop();
// now we draw the network
updateNetwork();
function updateNetwork(e) {
// draw the links
d3.select("svg.main").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})
.attr("transform", "translate(" + xOffset + "," + yOffset +")");
// draw the nodes
d3.select("svg.main").selectAll("circle")
.attr("cx", function (d) {return d.x; })
.attr("cy", function (d) {return d.y; })
.attr("transform", "translate(" + xOffset + "," + yOffset +")");
}
}
</script>
</body>
</html>
https://d3js.org/d3.v3.min.js