xxxxxxxxxx
<html>
<head>
<title>Clustering, betweenness centrality and eigenvector centrality</title>
<meta charset="utf-8" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script src="/emeeks/9915de8989e2a5c34652/7d22d8e4f05e944ced10408ae64579beff3c0858/jsnetworkx.js" type="text/javascript"></script>
<script src="https://cdn.jsdelivr.net/gh/eligrey/canvas-toblob.js/f1a01896135ab378aa5c0118eadd81da55e698d8/canvas-toblob.js"></script>
<script src="https://cdn.jsdelivr.net/gh/eligrey/filesaver.js/e9d941381475b5df8b7d7691013401e171014e89/filesaver.min.js"></script>
<script src="https://d3js.org/d3.v3.min.js"></script>
<style>
body {
font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
font-size: 14px;
line-height: 20px;
color: #333;
background-color: #fff;
}
div#canvas {
background-color: white;
border: 1px solid #999;
border-radius: 5px;
position: relative;
}
div#canvas .description {
color: #777;
position: absolute;
bottom: 0;
left: 0;
right: 0;
background-color: #EEE;
border-top: 1px solid #999;
border-bottom-left-radius: inherit;
border-bottom-right-radius: inherit;
padding: 5px;
}
svg.rule {
opacity: 1;
}
.edge line {
stroke: rgb(153, 153, 153);
stroke-width: 1.5px;
pointer-events: none;
}
marker {
fill: rgb(99,99,99);
stroke: rgb(153, 153, 153);
}
.node circle {
stroke-width: 2;
stroke: rgb(153, 153, 153);
fill: rgb(153, 153, 153);
cursor: pointer;
}
</style>
</head>
<body>
<div id="canvas">
<span class="description">Karate club graph</span>
<svg class="jsnx rule"></svg>
</div>
<button id="saveButton" style="position: absolute;top: 20px;left: 20px;">Export PNG</button>
</body>
<footer>
<script>
var rule_graph_file = "rule_graph.json",
data_error_file = "data_error.csv";
var color_map = {
"O": "rgb(255, 165, 0)",
"P": "rgb(255, 192, 203)",
"B": "rgb(0, 0, 255)",
"W": "rgb(255, 255, 255)",
"G": "rgb(0, 128, 0)",
"R": "rgb(255, 0, 0)",
"M": "rgb(255,0,255)",
"C": "rgb(0,255,255)"
}
var canvas_width = 940,
canvas_height = 480,
svg_rule_width = 1.00 * canvas_width,
svg_rule_height = canvas_height - 30.23;
d3.select('#saveButton').on('click', function(){
var svgString = getSVGString(d3.select("svg.rule").node());
svgString2Image( svgString, 10*svg_rule_width, 10*svg_rule_height, 'png', save ); // passes Blob and filesize String to the callback
function save( dataBlob, filesize ){
saveAs( dataBlob, 'D3 PNG.png' ); // FileSaver.js function
}
});
d3.select("div#canvas")
.style("width", canvas_width + 'px')
.style("height", canvas_height + 'px')
.select("svg.rule")
.style("width", svg_rule_width + 'px')
.style("height", svg_rule_height + 'px');
function encode_rule(rule) {
rule = rule.replace("==", "_eq_");
rule = rule.replace("!=", "_neq_");
rule = rule.replace(">", "_g_");
rule = rule.replace(">=", "_ge_");
rule = rule.replace("<", "_l_");
rule = rule.replace("<=", "_le_");
vov = rule.split("_");
return {
'rule': rule,
'variable': vov[0],
'operator': vov[1],
'value': vov[2]
};
}
function random_int(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
d3.json(rule_graph_file, function(error, graph) {
if (error) throw error;
graph.nodes.forEach(function(n) {
r = encode_rule(n.id);
n.rule = r.rule;
n.variable = r.variable;
n.operator = r.operator;
n.value = r.value;
});
var nodeHash = graph.nodes.reduce(function(map, n) {
map[n.id] = n;
return map;
}, {});
graph.links.forEach(function(e, i) {
e.id = i;
e.source = nodeHash[e.source];
e.target = nodeHash[e.target];
e.weight = 5;
});
create_rule_network(graph.nodes, graph.links);
});
function create_rule_network(nodes, edges) {
var rule_force = d3.layout.force()
.nodes(nodes)
.links(edges)
.size([svg_rule_width, svg_rule_height])
.charge(-300)
.chargeDistance(100)
.gravity(0.05)
.on("tick", update_rule_network)
.on('end', function() {
console.log('Layout finished. Start processing data...');
process_data_error(data_error_file, nodes, edges);
});
var rule_drag = rule_force.drag();
var svg = d3.select("svg.rule"),
g = svg.append("g");
svg.append("svg:defs").selectAll("marker")
.data(["end"])
.enter().append("svg:marker")
.attr("id", String)
.attr("viewBox", "0 -5 10 10")
.attr("refX", 15)
.attr("refY", -1.5)
.attr("markerWidth", 6)
.attr("markerHeight", 6)
.attr("orient", "auto")
.append("svg:path")
.attr("d", "M0,-5L10,0L0,5")
.attr("class", "arrow");
g.append("g").attr("class", "edges")
.selectAll("g.edge")
.data(edges, function (d) {return d.id})
.enter()
.append("g")
.attr("class", "edge")
.append("line")
.attr("marker-end", "url(#end)");
var g_nodes = g.append("g").attr("class", "nodes")
.selectAll("g.node")
.data(nodes)
.enter()
.append("g")
.attr("class", function (d) {return "node " + d.rule})
.attr("variable", function (d) {return d.variable})
.attr("operator", function (d) {return d.operator})
.attr("value", function (d) {return d.value})
.call(rule_drag);
g_nodes.append("circle")
.attr("r", 6);
g_nodes.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");
g_nodes.append("text")
.style("text-anchor", "middle")
.attr("y", 3)
.style("font-size", "8px")
.text(function (d) {return d.id})
.style("pointer-events", "none");
rule_force.start();
}
function update_rule_network(e) {
d3.select("svg.rule").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.rule").selectAll("g.node")
.attr("transform", function (d) {
return "translate(" + d.x + "," + d.y + ")";
});
}
function process_data_error(data_error_file, nodes, edges) {
d3.csv(data_error_file, function(error, data_error) {
process_record_wrapper(data_error, 0);
});
}
function process_record_wrapper(data_error, i) {
if (i >= data_error.length) return;
if (i > 10) return;
console.log("+++++ PROCESSING RECORD #" + (i+1) + " +++++");
process_record(data_error[i], function() {
process_record_wrapper(data_error, i+1);
});
}
function process_record(data, finish_callback) {
var nodes_el = d3.selectAll(".node")
.each(function(d) {
d.input = data[d.variable];
if (d.input == null || d.input == "") d.input = "NULL";
});
start_evaluation(Number.MAX_SAFE_INTEGER, finish_callback);
}
function start_evaluation(previous_error, finish_callback) {
var current_error = previous_error,
init_color_it = 0;
d3.selectAll(".node")
.selectAll("circle")
.each(function(d) {
if (d.id.startsWith("G") && d.id.endsWith("-in")) {
d.color = "P";
} else if(d.id.startsWith("G") && d.id.endsWith("-out")) {
d.color = "B";
} else if(d.id.startsWith("C")) {
d.color = "W";
} else {
d.color = "O";
}
init_color_it += 1;
})
.transition()
.duration(200)
.style("fill", function(d) {
return color_map[d.color]
})
.delay(100)
.each("end", function(d) {
init_color_it -= 1;
if (init_color_it == 0) {
validate_input(this, d, function() {
console.log(" => Set valid rule(s) to " + color_map["G"]);
calculate_centrality(function(error_rules) {
current_error = error_rules.length;
console.log(" => Set error rule(s): " + error_rules.join(", ") +
" to " + color_map["M"]);
}, function(max_centrality_nodes) {
console.log(" => Max centrality rule(s): " + max_centrality_nodes.join(", "));
make_revision(max_centrality_nodes, function(selected_node) {
console.log(" => Selected rule: " + selected_node);
}, function(variable, old_val, new_val) {
console.log(" => Value of variable " + variable +
" is revised from " + old_val +
" to " + new_val);
}, function() {
if (current_error < previous_error) {
start_evaluation(current_error, finish_callback);
} else {
console.log("FINISH");
finish_callback();
}
});
});
});
}
});
}
function validate_input(el, d, callback) {
var val_inp_iter = 0;
d3.selectAll(".node")
.selectAll("circle")
.filter(function(d) {
if (d.id.startsWith("R")) return true;
else return false;
})
.each(function(d) {
d.valid = false;
}).each(function(d) {
var str_value = d.input,
val = Number(str_value),
rule_val = Number(d.value);
if (d.value == "NULL" && str_value == "NULL") d.valid = true;
if (d.operator == "eq") {
if (d.value.indexOf("-") !== -1) {
var min_max = d.value.split("-");
try {
var min = Number(min_max[0]),
max = Number(min_max[1]);
if (val >= min && val <= max) d.valid = true;
} catch (err) {}
}
else if (d.value == str_value) d.valid = true;
}
if (val != NaN && rule_val != NaN) {
if (d.operator == "g") {
if (val > rule_val) d.valid = true;
} else if (d.operator == "ge") {
if (val >= rule_val) d.valid = true;
} else if (d.operator == "l") {
if (val < rule_val) d.valid = true;
} else if (d.operator == "le") {
if (val <= rule_val) d.valid = true;
}
}
}).filter(function(d) {
if (d.valid) return true;
else false;
})
.each(function(d) {
d.color = "G";
val_inp_iter += 1;
}).transition()
.duration(500)
.style("fill", function(d) {
return color_map[d.color]
}).delay(200)
.each("end", function(d) {
// Greenify the related group too
d3.selectAll("." + d.group + "-in,." + d.group + "-out")
.selectAll("circle")
.each(function(d) {
d.valid = true;
d.color = "G";
})
.transition()
.duration(500)
.style("fill", function(d) {
return color_map[d.color];
})
.delay(200)
.each("end", function(d) {});
val_inp_iter -= 1;
if (val_inp_iter == 0) {
callback();
}
});
}
function calculate_centrality(err_callback, cent_callback) {
var hyper_edges = {},
error_rules = [],
rule_error_iter = 0;
var err_nodes = d3.selectAll(".node")
.selectAll("circle")
.filter(function(d) {
if (d.id.startsWith("C")) return true;
return false
}).each(function(d) {
var source_variables = [],
target_variables = [],
is_target_error = false;
in_edges = d3.selectAll(".edge")
.filter(function(e) {
if (e.target.id == d.id) return true;
else return false;
}).each(function(f) {
// console.log(d.variable, f.source.id, f.source.color)
if (f.source.color == "G") {
if (f.source.rule.startsWith("G")) {
d3.selectAll(".edge")
.filter(function(e) {
if (e.target.id == f.source.id) return true;
return false;
})
.each(function(g) {
source_variables.push(g.source.variable);
});
} else {
source_variables.push(f.source.variable);
}
}
});
out_edges = d3.selectAll(".edge")
.filter(function(e) {
if (e.source.id == d.id) return true;
else return false;
}).each(function(d) {
if (d.target.rule.startsWith("G")) {
d3.selectAll(".edge")
.filter(function(e) {
if (e.source.id == d.target.id) return true;
return false;
})
.each(function(d) {
target_variables.push(d.target.variable);
});
} else {
target_variables.push(d.target.variable);
}
if (d.target.color == "G") is_target_error = true;
});
// console.log(d.rule, source_variables, target_variables)
d.source_variables = source_variables;
d.target_variables = target_variables;
if (source_variables.length != 0 && !is_target_error) {
d.error = true;
d.color = "M";
}
else d.error = false;
})
.each(function(d) {
var hyper_edge = [];
if (d.error) {
d.source_variables.forEach(function(sv) {
d.target_variables.forEach(function(tv) {
if(! hyper_edge.includes(sv)) {
hyper_edge.push(sv);
}
if(! hyper_edge.includes(tv)) {
hyper_edge.push(tv);
}
});
});
// console.log("Hyperedges : ", d.rule, hyper_edge);
d.hyper_edge = hyper_edge;
hyper_edges[d.rule] = hyper_edge;
error_rules.push(d.id);;
}
})
.filter(function(d) {
return d.error;
})
.each(function(d) {
rule_error_iter += 1;
})
.transition()
.duration(1000)
.attr("r", 12)
.style("fill", function(d) {
return color_map[d.color];
})
.delay(200)
.each("end", function(d) {
d3.select(this)
.transition()
.duration(1000)
.attr("r", 6);
rule_error_iter -= 1;
if (rule_error_iter == 0) {
err_callback(error_rules);
var max_centrality_nodes = [],
c_hyper_edges = JSON.parse(JSON.stringify(hyper_edges));
while(Object.keys(hyper_edges).length > 0) {
var key = Object.keys(hyper_edges)[0],
hyper_edge = hyper_edges[key],
keys_to_remove = [];
hyper_edge.forEach(function(n) {
if (! (max_centrality_nodes.includes(n))) {
max_centrality_nodes.push(n);
}
Object.keys(hyper_edges).forEach(function(k) {
var he = hyper_edges[k];
if (he.includes(n)) {
if (! (keys_to_remove.includes(k))) {
keys_to_remove.push(k);
}
}
});
});
keys_to_remove.forEach(function(k) {
delete hyper_edges[k]
});
}
var max = -1,
selected_nodes = [];
max_centrality_nodes.forEach(function(n) {
var count = 0;
Object.keys(c_hyper_edges).forEach(function(k) {
var he = c_hyper_edges[k];
if (he.includes(n)) {
count += 1;
}
});
// console.log(n, count)
if (count > max) {
selected_nodes = [];
selected_nodes.push(n);
max = count;
} else if (count == max) {
selected_nodes.push(n);
}
});
max_centrality_nodes = selected_nodes;
cent_callback(max_centrality_nodes)
}
});
}
function make_revision(max_centrality_nodes, cdt_callback, change_callback, finish_callback) {
var revision_iter = 0,
candidate = 0;
var candidate_nodes = d3.selectAll(".node")
.selectAll("circle")
.each(function(d) {
d.candidate = -1;
})
.filter(function(d) {
if (max_centrality_nodes.includes(d.variable)) return true;
else return false;
})
.each(function(d) {
d.candidate = candidate;
candidate += 1;
});
var sel_cdt_idx = random_int(0, candidate-1)
candidate_nodes.filter(function(d) {
if (d.candidate == sel_cdt_idx) {
d.color = "R";
cdt_callback(d.id)
return true;
}
return false;
})
.each(function() {
revision_iter += 1;
})
.transition()
.duration(1000)
.attr("r", 12)
.style("fill", function(d) {
return color_map[d.color];
})
.delay(200)
.each("end", function(d) {
d3.select(this)
.transition()
.duration(1000)
.attr("r", 6)
.delay(200)
.each("end", function(e) {
var val = d.value;
if (val.indexOf("-") !== -1) {
val = val.split("-")[0];
val = Number(val);
} else {
val = Number(val);
}
if (d.operator == "g") {
val += val;
} else if (d.operator == "l") {
val -= val;
}
change_callback(d.variable, d.input, val);
d.input = val;
// revision_iter -= 1;
// if (revision_iter == 0) {
finish_callback();
// }
});
});
}
function getSVGString( svgNode ) {
svgNode.setAttribute('xlink', 'https://www.w3.org/1999/xlink');
var cssStyleText = getCSSStyles( svgNode );
appendCSS( cssStyleText, svgNode );
var serializer = new XMLSerializer();
var svgString = serializer.serializeToString(svgNode);
svgString = svgString.replace(/(\w+)?:?xlink=/g, 'xmlns:xlink='); // Fix root xlink without namespace
svgString = svgString.replace(/NS\d+:href/g, 'xlink:href'); // Safari NS namespace fix
return svgString;
function getCSSStyles( parentElement ) {
var selectorTextArr = [];
// Add Parent element Id and Classes to the list
selectorTextArr.push( '#'+parentElement.id );
for (var c = 0; c < parentElement.classList.length; c++)
if ( !contains('.'+parentElement.classList[c], selectorTextArr) )
selectorTextArr.push( '.'+parentElement.classList[c] );
// Add Children element Ids and Classes to the list
var nodes = parentElement.getElementsByTagName("*");
for (var i = 0; i < nodes.length; i++) {
var id = nodes[i].id;
if ( !contains('#'+id, selectorTextArr) )
selectorTextArr.push( '#'+id );
var classes = nodes[i].classList;
for (var c = 0; c < classes.length; c++)
if ( !contains('.'+classes[c], selectorTextArr) )
selectorTextArr.push( '.'+classes[c] );
}
// Extract CSS Rules
var extractedCSSText = "";
for (var i = 0; i < document.styleSheets.length; i++) {
var s = document.styleSheets[i];
try {
if(!s.cssRules) continue;
} catch( e ) {
if(e.name !== 'SecurityError') throw e; // for Firefox
continue;
}
var cssRules = s.cssRules;
for (var r = 0; r < cssRules.length; r++) {
if ( contains( cssRules[r].selectorText, selectorTextArr ) )
extractedCSSText += cssRules[r].cssText;
}
}
return extractedCSSText;
function contains(str,arr) {
return arr.indexOf( str ) === -1 ? false : true;
}
}
function appendCSS( cssText, element ) {
var styleElement = document.createElement("style");
styleElement.setAttribute("type","text/css");
styleElement.innerHTML = cssText;
var refNode = element.hasChildNodes() ? element.children[0] : null;
element.insertBefore( styleElement, refNode );
}
}
function svgString2Image( svgString, width, height, format, callback ) {
var format = format ? format : 'png';
var imgsrc = 'data:image/svg+xml;base64,'+ btoa( unescape( encodeURIComponent( svgString ) ) ); // Convert SVG string to data URL
var canvas = document.createElement("canvas");
var context = canvas.getContext("2d");
canvas.width = width;
canvas.height = height;
var image = new Image();
image.onload = function() {
context.clearRect ( 0, 0, width, height );
context.drawImage(image, 0, 0, width, height);
canvas.toBlob( function(blob) {
var filesize = Math.round( blob.length/1024 ) + ' KB';
if ( callback ) callback( blob, filesize );
});
};
image.src = imgsrc;
}
</script>
</footer>
</html>
Updated missing url https://bl.ocks.org/emeeks/raw/9915de8989e2a5c34652/7d22d8e4f05e944ced10408ae64579beff3c0858/jsnetworkx.js to /emeeks/9915de8989e2a5c34652/7d22d8e4f05e944ced10408ae64579beff3c0858/jsnetworkx.js
Updated missing url https://cdn.rawgit.com/eligrey/canvas-toBlob.js/f1a01896135ab378aa5c0118eadd81da55e698d8/canvas-toBlob.js to https://cdn.jsdelivr.net/gh/eligrey/canvas-toblob.js/f1a01896135ab378aa5c0118eadd81da55e698d8/canvas-toblob.js
Updated missing url https://cdn.rawgit.com/eligrey/FileSaver.js/e9d941381475b5df8b7d7691013401e171014e89/FileSaver.min.js to https://cdn.jsdelivr.net/gh/eligrey/filesaver.js/e9d941381475b5df8b7d7691013401e171014e89/filesaver.min.js
https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js
https://bl.ocks.org/emeeks/raw/9915de8989e2a5c34652/7d22d8e4f05e944ced10408ae64579beff3c0858/jsnetworkx.js
https://cdn.rawgit.com/eligrey/canvas-toBlob.js/f1a01896135ab378aa5c0118eadd81da55e698d8/canvas-toBlob.js
https://cdn.rawgit.com/eligrey/FileSaver.js/e9d941381475b5df8b7d7691013401e171014e89/FileSaver.min.js
https://d3js.org/d3.v3.min.js