Commit 1642b3ed authored by Kautsar, Satria's avatar Kautsar, Satria
Browse files

Merge branch 'html_visualization' into 'master'

Html visualization

See merge request medema-group/BiG-SCAPE!8
parents fa54dd9a d42b9109
......@@ -1148,6 +1148,7 @@ def clusterJsonBatch(bgcs, outputFileBase,matrix,cutoffs=[1.0],damping=0.8,clust
bgc2simIdx = dict(zip(bgcs, range(len(bgcs))))
pfam_domain_categories = os.path.join(os.path.dirname(os.path.realpath(__file__)), "Pfam-A.clans.tsv")
pfam_descrs = generatePfamDescriptionsMatrix(pfam_domain_categories)
pfam_colors = generatePfamColorsMatrix(os.path.join(os.path.dirname(os.path.realpath(__file__)), "domains_color_file.tsv"))
# Get info on all BGCs to export to .js for visualization
bs_data = []
......@@ -1200,16 +1201,13 @@ def clusterJsonBatch(bgcs, outputFileBase,matrix,cutoffs=[1.0],damping=0.8,clust
entry = line.split('\t')
orf = entry[-1].strip().split(':')[0]
pfamID = entry[5].split('.')[0]
pfamDescr = pfam_descrs.get(pfamID,None)
if pfamDescr:
orfDict[orf]["domains"].append({'code': '{} : {}'.format(pfamID,pfamDescr),'start':int(entry[3]),'end':int(entry[4]),'bitscore': float(entry[1])})
else:
orfDict[orf]["domains"].append({'code': entry[5], 'start': int(entry[3]), 'end': int(entry[4]), 'bitscore': float(entry[1])})
orfDict[orf]["domains"].append({'code': pfamID, 'start': int(entry[3]), 'end': int(entry[4]), 'bitscore': float(entry[1])})
bgcJsonDict[bgcName]['orfs'] = list(orfDict.values())
bs_data = [bgcJsonDict[clusterNames[int(bgc)]] for bgc in bgcs]
## Write html output folder structure (and update bigscape_classes.js) for this module
## Write {modulename}/bgcs.js
## Write js/pfams.js
assert htmlFolder != None
module_name = outputFileBase.split(os.path.sep)[-1]
module_html_path = os.path.join(html_folder, "networks", module_name)
......@@ -1217,6 +1215,16 @@ def clusterJsonBatch(bgcs, outputFileBase,matrix,cutoffs=[1.0],damping=0.8,clust
with open(os.path.join(module_html_path, "bs_data.js"), "w") as bs_data_js:
bs_data_js.write("var bs_data={};\n".format(json.dumps(bs_data, indent=4, separators=(',', ':'), sort_keys=True)))
add_to_bigscape_classes_js(module_name, className, ["cutoff{:4.2f}".format(cutoff) for cutoff in cutoffs], htmlFolder)
with open(os.path.join(html_folder, "js", "pfams.js"), "w") as pfams_js:
pfam_json = {}
for pfam_code in pfam_colors:
pfam_obj = {}
pfam_obj["col"] = pfam_colors[pfam_code]
pfam_obj["desc"] = ""
if pfam_code in pfam_descrs:
pfam_obj["desc"] = pfam_descrs[pfam_code]
pfam_json[pfam_code] = pfam_obj
pfams_js.write("var pfams={};\n".format(json.dumps(pfam_json, indent=4, separators=(',', ':'), sort_keys=True)))
for cutoff in cutoffs:
simMatrix = lil_matrix((len(bgcs), len(bgcs)), dtype=np.float32)
......
......@@ -541,6 +541,30 @@ def generatePfamDescriptionsMatrix(pfam_domain_categories):
return pfam_descriptions
def generatePfamColorsMatrix(pfam_domain_colors):
'''
:param pfam_domain_colors: tab-delimited file
:return: dictionary with pfam ID as key and rgb colors as value
'''
pfam_colors = {}
if os.path.isfile(pfam_domain_colors):
print(" Found file with Pfam domain colors")
with open(pfam_domain_colors, "r") as cat_handle:
for line in cat_handle:
# handle comments and empty lines
if line[0] != "#" and line.strip():
row = line.strip().split("\t")
domain = row[0]
rgb = row[-1]
pfam_colors[domain] = rgb
else:
print(" File pfam_domain_colors was NOT found")
return pfam_colors
def copy_html_template(html_folder):
if (os.path.isdir(html_folder)):
shutil.rmtree(html_folder)
......
......@@ -2,6 +2,9 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv='cache-control' content='no-cache'>
<meta http-equiv='expires' content='0'>
<meta http-equiv='pragma' content='no-cache'>
<title>BiG-SCAPE result XXX</title>
<link rel="stylesheet" type="text/css" href="./css/style.css">
</head>
......@@ -22,7 +25,7 @@
</div>
<div class="page">
<div id="buttons">
<span id="cluster-type" style="float: left; padding-top: 0.2em;">Network:</span>
<span id="cluster-type" style="float: left; padding-top: 0.2em;">Networks:</span>
<ul id="clusterbuttons" style="float: left;">
<li class="clbutton overview selected"><a href="javascript:loadOverview();">Overview</a></li>
</ul>
......@@ -72,14 +75,14 @@
var dropmenu = $("<div class='cldropdown'></div>");
for (var j in module["results"]) {
var res = module["results"][j];
var a2 = $("<a href='#'>" + res + "</a>");
a2.click({ module: module }, function(handler){
$(".clbutton").removeClass("selected");
$(handler.target).parent().parent().addClass("selected");
$(".clbutton:not(.selected)").addClass("inactive");
$(handler.target).parent().parent().removeClass("inactive");
loadNetwork(handler.data.module["name"], res);
});
var a2 = $("<a href='#'>" + res + "</a>");
a2.click({ module: module, res: res }, function(handler){
$(".clbutton").removeClass("selected");
$(handler.target).parent().parent().addClass("selected");
$(".clbutton:not(.selected)").addClass("inactive");
$(handler.target).parent().parent().removeClass("inactive");
loadNetwork(handler.data.module["name"], handler.data.res);
});
dropmenu.append(a2);
}
li.addClass(module["css"]);
......@@ -110,4 +113,4 @@
});
});
</script>
</html>
</html>
\ No newline at end of file
/* Copyright 2017 Satria Kautsar */
var Arrower = {
version: "0.0.0",
required: [
"jquery",
"svg.js"
],
tooltip_id: "arrower-tooltip-1234567890"
version: "0.0.0",
required: [
"jquery",
"svg.js",
"pfams.js"
],
tooltip_id: "arrower-tooltip-1234567890"
};
if (pfams === undefined) {
pfams = {};
}
Arrower.drawClusterSVG = (function(cluster, height = 40) {
var container = document.createElement("div");
var draw = SVG(container).size('100%', height).group();
var scale = (function(val) { return parseInt(val / (1000 / height)); })
var container = document.createElement("div");
var draw = SVG(container).size('100%', height).group();
var scale = (function(val) { return parseInt(val / (1000 / height)); })
// draw line
draw.line(0, parseInt(height / 2), scale(cluster.end - cluster.start), parseInt(height / 2)).stroke({width: 1});
var width = scale(cluster.end - cluster.start);
if (cluster.hasOwnProperty("orfs")) {
// draw arrows
for (var i in cluster.orfs) {
var orf = cluster.orfs[i];
var orf_color = "white";//"gray";
if (orf.hasOwnProperty("color")) {
orf_color = orf.color;
}
var pol = draw.polygon(Arrower.toPointString(Arrower.getArrowPoints(orf, cluster, height, scale)))
.fill(orf_color)
.stroke({width: 1})
.addClass("arrower-orf");
$(pol.node).mouseover({orf: orf}, function(handler){
var start = handler.data.orf.start;
var end = handler.data.orf.end;
Arrower.showToolTip("ORF: " + handler.data.orf.id + "<br/>" + start + " - " + end, handler);
$(handler.target).css("stroke-width", "3px");
$(handler.target).css("stroke", "red");
handler.stopPropagation();
});
$(pol.node).mouseleave(function(handler){
$(handler.target).css("stroke-width", "1px");
$(handler.target).css("stroke", "black");
$("#" + Arrower.tooltip_id).css("display", "none");
});
// draw line
draw.line(0, parseInt(height / 2), scale(cluster.end - cluster.start), parseInt(height / 2)).stroke({width: 1});
var width = scale(cluster.end - cluster.start);
if (orf.hasOwnProperty("domains")) {
// draw domains
for (var j in orf.domains) {
var domain = orf.domains[j];
var color = "gray";
if (domain.hasOwnProperty("color")) {
color = domain.color;
}
var dom = draw.polygon(Arrower.toPointString(Arrower.getDomainPoints(domain, orf, cluster, height, scale)))
.fill(color)
.stroke({width: 1})
.addClass("arrower-domain");
$(dom.node).mouseover({domain: domain}, function(handler){
var start = handler.data.domain.start;
var end = handler.data.domain.end;
Arrower.showToolTip("Domain: " + handler.data.domain.code + " (" + domain.bitscore + ")" + "<br/>" + start + " - " + end, handler);
$(handler.target).css("stroke-width", "3px");
$(handler.target).css("stroke", "red");
handler.stopPropagation();
});
$(dom.node).mouseleave(function(handler){
$(handler.target).css("stroke-width", "1px");
$(handler.target).css("stroke", "black");
$("#" + Arrower.tooltip_id).css("display", "none");
});
if (cluster.hasOwnProperty("orfs")) {
// draw arrows
for (var i in cluster.orfs) {
var orf = cluster.orfs[i];
var orf_color = "white";//"gray";
if (orf.hasOwnProperty("color")) {
orf_color = orf.color;
}
var pol = draw.polygon(Arrower.toPointString(Arrower.getArrowPoints(orf, cluster, height, scale)))
.fill(orf_color)
.stroke({width: 1})
.addClass("arrower-orf");
$(pol.node).mouseover({orf: orf}, function(handler){
var start = handler.data.orf.start;
var end = handler.data.orf.end;
Arrower.showToolTip("ORF: " + handler.data.orf.id + "<br/>" + start + " - " + end, handler);
$(handler.target).css("stroke-width", "3px");
$(handler.target).css("stroke", "red");
handler.stopPropagation();
});
$(pol.node).mouseleave(function(handler){
$(handler.target).css("stroke-width", "1px");
$(handler.target).css("stroke", "black");
$("#" + Arrower.tooltip_id).css("display", "none");
});
if (orf.hasOwnProperty("domains")) {
// draw domains
for (var j in orf.domains) {
var domain = orf.domains[j];
var color = "gray";
if (domain.hasOwnProperty("color")) {
color = domain.color;
} else if (pfams.hasOwnProperty(domain.code)) {
color = "rgb(" + pfams[domain.code]["col"] + ")";
}
var dom = draw.polygon(Arrower.toPointString(Arrower.getDomainPoints(domain, orf, cluster, height, scale)))
.fill(color)
.stroke({width: 1})
.addClass("arrower-domain");
$(dom.node).mouseover({domain: domain}, function(handler){
var start = handler.data.domain.start;
var end = handler.data.domain.end;
var dom_desc = "";
if (handler.data.domain.hasOwnProperty("desc")) {
dom_desc = handler.data.domain.color;
} else if (pfams.hasOwnProperty(handler.data.domain.code)) {
dom_desc = pfams[handler.data.domain.code]["desc"];
}
Arrower.showToolTip("Domain: " + handler.data.domain.code + " " + dom_desc + " (" + handler.data.domain.bitscore + ")" + "<br/>" + start + " - " + end, handler);
$(handler.target).css("stroke-width", "3px");
$(handler.target).css("stroke", "red");
handler.stopPropagation();
});
$(dom.node).mouseleave(function(handler){
$(handler.target).css("stroke-width", "1px");
$(handler.target).css("stroke", "black");
$("#" + Arrower.tooltip_id).css("display", "none");
});
}
}
}
}
$(draw.node).parent().mouseover({domain: domain}, function(handler){
var bgc_desc = "<b>BGC: " + cluster.id + "</b>";
if (cluster.hasOwnProperty("desc")) {
bgc_desc += "<br /> " + cluster["desc"];
}
Arrower.showToolTip(bgc_desc, handler);
});
$(draw.node).parent().mouseleave(function(handler){
$("#" + Arrower.tooltip_id).css("display", "none");
});
$(draw.node).parent().mouseover({domain: domain}, function(handler){
var bgc_desc = "<b>BGC: " + cluster.id + "</b>";
if (cluster.hasOwnProperty("desc")) {
bgc_desc += "<br /> " + cluster["desc"];
}
Arrower.showToolTip(bgc_desc, handler);
});
$(draw.node).parent().mouseleave(function(handler){
$("#" + Arrower.tooltip_id).css("display", "none");
});
$(container).find("svg").attr("width", width + "px");
return $(container).find("svg")[0];
$(container).find("svg").attr("width", width + "px");
return $(container).find("svg")[0];
});
Arrower.getOrfPoints = (function(orf, cluster, height, scale){
var x_points = [
scale(orf.start),
(orf.strand === 0) ?
(scale(orf.start) + scale(orf.end - orf.start))
: ((scale(orf.end - orf.start) > (height / 2)) ?
(scale(orf.start) + Math.max(scale(orf.end - orf.start - ((orf.end - orf.start) / 4)), (scale(orf.end - orf.start) - parseInt(height / 2))))
: scale(orf.start)),
var x_points = [
scale(orf.start),
(orf.strand === 0) ?
(scale(orf.start) + scale(orf.end - orf.start))
];
var y_points = [
(orf.strand === 0) ?
((height / 2) - (height / 3))
: ((height / 2) - (height / 3)) - (height / 5),
((height / 2) - (height / 3)),
(height / 2),
((height / 2) + (height / 3)),
(orf.strand === 0) ?
((height / 2) + (height / 3))
: ((height / 2) + (height / 3)) + (height / 5)
];
: ((scale(orf.end - orf.start) > (height / 2)) ?
(scale(orf.start) + Math.max(scale(orf.end - orf.start - ((orf.end - orf.start) / 4)), (scale(orf.end - orf.start) - parseInt(height / 2))))
: scale(orf.start)),
(scale(orf.start) + scale(orf.end - orf.start))
];
var y_points = [
(orf.strand === 0) ?
((height / 2) - (height / 3))
: ((height / 2) - (height / 3)) - (height / 5),
((height / 2) - (height / 3)),
(height / 2),
((height / 2) + (height / 3)),
(orf.strand === 0) ?
((height / 2) + (height / 3))
: ((height / 2) + (height / 3)) + (height / 5)
];
return { x: x_points, y: y_points };
return { x: x_points, y: y_points };
});
Arrower.getArrowPoints = (function(orf, cluster, height, scale) {
var points = [];
var pts = Arrower.getOrfPoints(orf, cluster, height, scale);
var points = [];
var pts = Arrower.getOrfPoints(orf, cluster, height, scale);
points.push({ // blunt start
x: pts.x[0],
y: pts.y[1]
});
points.push({ // junction top
x: pts.x[1],
y: pts.y[1]
});
points.push({ // junction top-top
x: pts.x[1],
y: pts.y[0]
});
points.push({ // pointy end
x: pts.x[2],
y: pts.y[2]
});
points.push({ // junction bottom-bottom
x: pts.x[1],
y: pts.y[4]
});
points.push({ // junction bottom
x: pts.x[1],
y: pts.y[3]
});
points.push({ // blunt end
x: pts.x[0],
y: pts.y[3]
});
points.push({ // blunt start
x: pts.x[0],
y: pts.y[1]
});
points.push({ // junction top
x: pts.x[1],
y: pts.y[1]
});
points.push({ // junction top-top
x: pts.x[1],
y: pts.y[0]
});
points.push({ // pointy end
x: pts.x[2],
y: pts.y[2]
});
points.push({ // junction bottom-bottom
x: pts.x[1],
y: pts.y[4]
});
points.push({ // junction bottom
x: pts.x[1],
y: pts.y[3]
});
points.push({ // blunt end
x: pts.x[0],
y: pts.y[3]
});
if (orf.strand < 0) {
points = Arrower.flipHorizontal(points, scale(orf.start), (scale(orf.start) + scale(orf.end - orf.start)));
}
if (orf.strand < 0) {
points = Arrower.flipHorizontal(points, scale(orf.start), (scale(orf.start) + scale(orf.end - orf.start)));
}
return points;
return points;
});
Arrower.getDomainPoints = (function(domain, orf, cluster, height, scale) {
var points = [];
var arrow_pts = Arrower.getArrowPoints(orf, cluster, height, scale);
if (orf.strand < 0) {
arrow_pts = Arrower.flipHorizontal(arrow_pts, scale(orf.start), (scale(orf.start) + scale(orf.end - orf.start)));
}
arrow_pts.splice(3, 0, arrow_pts[3]); // convert into bluntish-end arrow
var domain_x = {
start: (scale(orf.start) + scale(domain.start * 3)),
end: (scale(orf.start) + scale(domain.end * 3))
};
var points = [];
var arrow_pts = Arrower.getArrowPoints(orf, cluster, height, scale);
if (orf.strand < 0) {
arrow_pts = Arrower.flipHorizontal(arrow_pts, scale(orf.start), (scale(orf.start) + scale(orf.end - orf.start)));
}
arrow_pts.splice(3, 0, arrow_pts[3]); // convert into bluntish-end arrow
var domain_x = {
start: (scale(orf.start) + scale(domain.start * 3)),
end: (scale(orf.start) + scale(domain.end * 3))
};
var getY = function(x) {
var m = Math.abs(arrow_pts[5].y - arrow_pts[4].y) / Math.abs(arrow_pts[5].x - arrow_pts[4].x);
return (m * (x - arrow_pts[4].x));
}
var getY = function(x) {
var m = Math.abs(arrow_pts[5].y - arrow_pts[4].y) / Math.abs(arrow_pts[5].x - arrow_pts[4].x);
return (m * (x - arrow_pts[4].x));
}
for (var i in arrow_pts) {
var apt = arrow_pts[i];
var new_point = {};
new_point.x = apt.x < domain_x.start ? domain_x.start : (apt.x > domain_x.end ? domain_x.end : apt.x);
new_point.y = (new_point.x < arrow_pts[1].x) ?
Math.min(Math.max(apt.y, arrow_pts[0].y), arrow_pts[7].y)
:((new_point.x == arrow_pts[1].x) ?
apt.y
:(i < 4 ?
(arrow_pts[3].y + getY(new_point.x))
:(arrow_pts[3].y - getY(new_point.x))));
points.push(new_point);
}
for (var i in arrow_pts) {
var apt = arrow_pts[i];
var new_point = {};
new_point.x = apt.x < domain_x.start ? domain_x.start : (apt.x > domain_x.end ? domain_x.end : apt.x);
new_point.y = (new_point.x < arrow_pts[1].x) ?
Math.min(Math.max(apt.y, arrow_pts[0].y), arrow_pts[7].y)
:((new_point.x == arrow_pts[1].x) ?
apt.y
:(i < 4 ?
(arrow_pts[3].y + getY(new_point.x))
:(arrow_pts[3].y - getY(new_point.x))));
points.push(new_point);
}
if (orf.strand < 0) {
points = Arrower.flipHorizontal(points, scale(orf.start), (scale(orf.start) + scale(orf.end - orf.start)));
}
if (orf.strand < 0) {
points = Arrower.flipHorizontal(points, scale(orf.start), (scale(orf.start) + scale(orf.end - orf.start)));
}
return points;
return points;
});
Arrower.flipHorizontal = (function(points, leftBound, rightBound) {
var new_points = [];
var new_points = [];
for(var i in points) {
var point = points[i];
if ((point.x < leftBound) || (point.x > rightBound)) {
throw "Error flipping points";
}
new_points.push({ x: rightBound - (point.x - leftBound), y: point.y });
for(var i in points) {
var point = points[i];
if ((point.x < leftBound) || (point.x > rightBound)) {
throw "Error flipping points";
}
new_points.push({ x: rightBound - (point.x - leftBound), y: point.y });
}
return new_points;
return new_points;
});
Arrower.toPointString = (function(points) {
points_string = "";
points_string = "";
for(var i in points) {
var point = points[i];
if (i > 0) {
points_string += ", ";
}
points_string += parseInt(point.x) + "," + parseInt(point.y);
for(var i in points) {
var point = points[i];
if (i > 0) {
points_string += ", ";
}
points_string += parseInt(point.x) + "," + parseInt(point.y);
}
return points_string;
return points_string;
});
Arrower.getRandomCluster = (function() {
function random(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min)) + min;
}
var cl_start = 23000;
var cl_end = 23000 + random(5000, 50000);
function random(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min)) + min;
}
var cl_start = 23000;
var cl_end = 23000 + random(5000, 50000);
var orfs = [];
var num_orfs = random(5, 20);
for (var i = 0; i < num_orfs; i++) {
var pos1 = random(i * ((cl_end - cl_start) / num_orfs), (i + 1) * ((cl_end - cl_start) / num_orfs));
var pos2 = random(i * ((cl_end - cl_start) / num_orfs), (i + 1) * ((cl_end - cl_start) / num_orfs));
if (Math.abs(pos1 - pos2) < 200) {
continue;
}
var orf_start = Math.min(pos1, pos2);
var orf_end = Math.max(pos1, pos2);
var orf_strand = Math.random() > 0.5? 1 : -1;//random(-1, 2);
var orf_type = Math.random() > 0.5? "biosynthetic" : "others";
var orf_domains = [];
var num_domains = random(0, 4);
for (var j = 0; j < num_domains; j++) {
var dpos1 = random(j * ((orf_end - orf_start) / num_domains), (j + 1) * ((orf_end - orf_start) / num_domains));
var dpos2 = random(j * ((orf_end - orf_start) / num_domains), (j + 1) * ((orf_end - orf_start) / num_domains));
var dom_start = Math.min(dpos1, dpos2);
var dom_end = Math.max(dpos1, dpos2);
orf_domains.push({