d3.svg.hotTable = function(id, rData){ /* rData: { "table":[ // Log(exp) GeneName domestic url Ensemble ID Plot [1.85, ["RNA", "/web/?a=1&b=c"], "ENSG00000132341", "url"], [ ... ... ], ... ... ], "map":{ x: ["x1", "x2", "x3", ... ], // y --> referance GeneName coloum dots: [ [1.5, 2.3, 5.6, 0.3, 4.5, .... ], [ ... ... ], ... ... ] } } */ var tooltip = document.getElementById("tooltip"); if(!tooltip){ tooltip = document.createElement("div"); tooltip.setAttribute("id", "tooltip"); tooltip.setAttribute("class", "tooltip"); document.body.appendChild(tooltip); } var tableData, mapData, tRows, mapCols, thead = 0, tbody = 0, // Stable link to Ensembl: http://www.ensembl.org/info/docs/webcode/linking.html stableLink = "http://www.ensembl.org/id/"; if(!rData){ alert("Data is EMPTY!"); return; }else{ tableData = rData.table; rData.map.y = rData.table.map(function(d){ return d[1][0]; }); mapData = rData.map; tRows = tableData.length; mapCols = mapData.x.length; } if(mapData.x.length !== mapData.dots[0].length){ alert("Error Data - the items counts not martch with content\n" + "mapData.x.length: " + mapData.x.length + "\n" + "mapData.dots[0].length: " + mapData.dots[0].length); return; } var ul = d3.select("#ge-hot-table").html(""); ul.append("li").html(makeAtable()); // Detecting if the table is rendering thoroutly. // If rending is finished then drawing heatmap. var timeID = setInterval(function(){ var table = ul.select("table").node(); var th = table.tHead.getBoundingClientRect().height, tb = table.tBodies[0].getBoundingClientRect().height; if((tbody < tb) || (thead < th)){ tbody = tb; thead = th; }else{ clearInterval(timeID); makeAmap(tb, th); } }, 50); function makeAtable(){ var tr = tableData.map(function(row){ return "" + row[0] + "" + "" + turncate(row[1][0]) + "" + "" + row[2] + "" + "" + "Plot"; }); function turncate(text, len){ if(!len){ len = 8; } if(text.length <= len){ return text; }else{ return text.slice(0, len - 3) + "..."; } } return "" + "" + tr.join("") + "
Log(exp)GeneEnsemble ID 
"; } function makeAmap(tbodyHeight, theadHeight){ // data processing var rects = mapData.dots.map(function(row, y){ return row.map(function(cell, x){ return {x: mapData.x[x], y: mapData.y[y], z: cell}; }); }); var zExtent = d3.extent(Array.prototype.concat.apply([], mapData.dots.map(function(d){ return d3.extent(d); }))); var svgWidth, legendWidth = 70, minRectWidth = 10, rectHeight = tbodyHeight / tRows, colors = ["#3060cf", "#fffbbc", "#c4463a"]; if(minRectWidth * mapCols < (615 - legendWidth)){ svgWidth = 615; minRectWidth = (svgWidth - legendWidth) / mapCols; }else{ svgWidth = minRectWidth * mapCols + legendWidth; } var cScale = d3.scale.linear() .domain([zExtent[0], 0.5 * (zExtent[0] + zExtent[1]), zExtent[1]]) .range(colors); // append a div whithhold the height of table header var li2 = ul.append("li"); li2.append("div").style({ display: "inline-block", height: theadHeight + "px", width: "100%", margin: 0 }); var svg = li2 .append("svg") .attr({ width : svgWidth, height : tbodyHeight, version : 1.1, xmlns : "http://www.w3.org/2000/svg" }); svg.append("defs") .append("linearGradient") .attr({ id: "upwards", x1: "0%", y1: "100%", x2: "0%", y2: "0%" }) .call(function(s){ var offset = ["0%", "50%", "100%"]; colors.forEach(function(color, index){ s.append("stop").attr({offset: offset[index], "stop-color": color}); }); }); var mainPlot = svg.append("g").attr("class", "ge-main-plot"), gLegend = svg.append("g").attr({ class: "ge-legend", transform: "translate(" + [svgWidth - legendWidth, 0] + ")", "shape-rendering": "crispEdges" }); mainPlot.selectAll("g.ge-row") .data(rects) .enter() .append("g") .attr({ transform : function(d,i){ return "translate(0," + (i * rectHeight) + ")"; }, class : "ge-row" }) .selectAll("rect") .data(function(d){return d;}) .enter() .append("rect") .attr({ width : minRectWidth, height : rectHeight, stroke : "#333", "shape-rendering" : "crispEdges", "stroke-width" : 1, x : function(d,i){ return i * minRectWidth; }, fill : function(d){ return cScale(d.z); } }) .on("mousemove", onMouseMove) .on("mouseleave", onMouseLeave); //gLegend var nonius = 5, dPath = "M0 0 " + "L" + -nonius + " " + -nonius + " " + "L" + -2 * nonius + " " + -nonius + " " + "L" + -2 * nonius + " " + nonius + " " + "L" + -nonius + " " + nonius + " Z"; var legendRectHeight, moveScale, marker, lAxis, gRect, axisTicks; if(tRows > 5){ legendRectHeight = 5 * (tbodyHeight / tRows); axisTicks = 5; }else{ legendRectHeight = tbodyHeight - 15; axisTicks = 3; } gRect = gLegend.append("g") .attr("transform", "translate(" + [20, 0.5 * (tbodyHeight - legendRectHeight)] + ")"); moveScale = d3.scale.linear().domain(zExtent).range([0, legendRectHeight]); lAxis = d3.svg.axis().scale(moveScale.copy().range([legendRectHeight,0])).orient("right").ticks(axisTicks); marker = gRect.append("path").attr({fill: "steelblue", d: dPath}).style("display", "none"); gRect.append("rect").attr({width: 10, height: legendRectHeight, fill: "url(#upwards)"}); gRect.append("text").text("Exp.").attr({dx: -2, dy: -15}); gRect.append("g") .attr("transform", "translate(10,0)") .call(lAxis) .call(function(s){ s.select("path").attr("fill", "none"); s.selectAll("line").attr({stroke: "#ccc", "stroke-width": 1}); }); // Events functions function onMouseMove(d){ d3.select(this).attr({stroke: "#000", "stroke-width": 2}); var msg = [d.x, "Gene: " + d.y, "Expression: " + d.z], coordinate = [d3.event.clientX, d3.event.clientY]; msg = "" + msg.join("") + ""; var box = tooltip.getBoundingClientRect(); d3.select(this).classed("highlight", true); d3.select(tooltip) .html(msg) .style({ display : "block", left : coordinate[0] - box.width / 2 + "px", top : coordinate[1] - box.height - 10 + "px" }); marker.attr("transform", "translate(0," + (legendRectHeight - moveScale(d.z)) + ")") .style("display", "block"); } function onMouseLeave(){ d3.select(this).attr({stroke: "#333", "stroke-width": 1}); tooltip.style.left = "-99999px"; d3.select(this).classed("highlight", false); marker.style("display", "none"); } } // heatmap END }; // hotTable END d3.svg.jitterPlot = function (id, rData){ var svgWidth = 550, svgHeight = 300, margin = {top: 20, right: 30, bottom: 50, left: 70}, width = svgWidth - margin.left - margin.right, height = svgHeight - margin.top - margin.bottom, radius = 3, fillColor = "steelblue", f = d3.format(".4f"); // data proccessing var yExtent = d3.extent(Array.prototype.concat.apply([], rData.data.map(function(d){ return d.slice(1); }))); var pData = rData.data.map(function(d){ var y = d.slice(1).sort(function(a,b){ return a - b; }); return { name : d[0], y : y, quantile: getQuantiles(y.slice()) }; }); var yScale = d3.scale.linear().domain(yExtent).range([0, height]).nice(), yAxis = d3.svg.axis().orient("left") .tickSize(-width, -width) .scale(yScale.copy().nice().range([height, 0])), xScale = d3.scale.ordinal() .domain(pData.map(function(d){ return d.name; })) .rangeBands([0, width], 0.1, 0.1), xAxis = d3.svg.axis().orient("bottom").scale(xScale).tickSize(0,0).tickPadding(10), rangeBand = xScale.rangeBand(), boxWidth = rangeBand * 0.8, whiskerWidth = boxWidth * 0.5; var svg, plotMain, gXAxis, gYAxis, gTooltip, filterID; if(typeof id === "string"){ svg = d3.select("#" + id).append("svg"); }else if(id instanceof HTMLElement){ svg = d3.select(id).append("svg"); }else{ alert("Invalid ID!"); return; } svg.attr({ width : svgWidth, height : svgHeight, version : 1.1, xmlns : "http://www.w3.org/2000/svg" }) .style("font", "12px arial, sans-serif"); plotMain = svg.append("g").attr({ class: "gen-plot-main", transform: "translate("+ [margin.left, margin.top] +")" }); gXAxis = plotMain.append("g").attr({ class : "gen-axis xaxis", transform : "translate(0," + height + ")", "shape-rendering" : "crispEdges" }); gYAxis = plotMain.append("g").attr({ class : "gen-axis yaxis", "shape-rendering" : "crispEdges" }); gXAxis.call(xAxis).call(uniform); gYAxis.call(yAxis).call(uniform); gYAxis.append("g") .attr("transform", "translate(" + [-50, 0.5 * height] + ")") .append("text") .html("" + rData.gene + "Expression FPKM") .attr({"text-anchor": "middle", transform: "rotate(-90)"}); plotMain .selectAll("g.gen-plotbox") .data(pData) .enter() .append("g") .attr({ class: "gen-plotbox", transform: function(d,i){ return "translate(" + [xScale.range()[i] + 0.5 * rangeBand, height] + ")"; } }) .on("mouseover", onMouseOver) .on("mouseleave", onMouseLeave) .each(function(D){ var self = d3.select(this); if(D.y.length > 5){ self .append("line") .attr({ class : "Q4 Q-line", x1 : -0.5 * whiskerWidth, x2 : 0.5 * whiskerWidth, y1 : -yScale(D.quantile[4]), y2 : -yScale(D.quantile[4]) }); self .append("line") .attr({ class : "Q0 Q-line", x1 : -0.5 * whiskerWidth, x2 : 0.5 * whiskerWidth, y1 : -yScale(D.quantile[0]), y2 : -yScale(D.quantile[0]) }); self .append("line") .attr({ class : "center-line Q-line", x1 : 0, x2 : 0, y1 : -yScale(D.quantile[4]), y2 : -yScale(D.quantile[0]) }); self .append("rect") .attr({ class : "Q-box", x : -0.5 * boxWidth, y : -yScale(D.quantile[3]), width : boxWidth, height : yScale(D.quantile[3]) - yScale(D.quantile[1]), fill : "#fff", stroke : "#000", "stroke-width": 1 }); self .append("line") .attr({ class : "Q2 Q-median", x1 : -0.5 * boxWidth, x2 : 0.5 * boxWidth, y1 : -yScale(D.quantile[2]), y2 : -yScale(D.quantile[2]), stroke : "#000", "stroke-width": 2 }); } // circles D.y.forEach(function(d){ self .append("circle") .attr({ class : "jitter-circle", cx : 0, cy : 0, r : 0, fill : fillColor, stroke : fillColor, "stroke-width": 1, "fill-opacity": 0.1 }) .transition() .duration(700) .attr({ cx: Math.random() * whiskerWidth - 0.5 * whiskerWidth, cy: -yScale(d), r : radius }); }); }); plotMain.selectAll(".Q-line").attr({stroke: "#000", "stroke-width": 1}); /* */ filterID = "gen-" + (new Date()).getTime(); svg.insert("filter", "g:first-child") .attr({ id: filterID }) .html(''); gTooltip = plotMain .append("g") .attr({class: "gen-tooltip", "shape-rendering": "crispEdges"}); function onMouseOver(d){ var html = "", dy = 15, labels = ["Max: ", "Q3: ", "Median: ", "Q1: ", "Min: "], point = d3.mouse(plotMain.node()), path, textBox; d.quantile.slice().reverse().forEach(function(e,i){ html += "" + labels[i] + f(e) + ""; }); gTooltip.html("" + html + ""); textBox = gTooltip.node().getBBox(); path = gTooltip.insert("path", "text").attr({ fill : "white", stroke : "black", //filter : "url(#" + filterID + ")", "stroke-width": 1, "fill-opacity": 0.8, transform : "translate(" + [-0.5 * dy, -0.2 * dy] + ")" }); if(point[1] > textBox.height){ path.attr("d", getTooltipBox([textBox.width + dy, textBox.height + dy]), "down"); gTooltip .transition() .delay(50) .attr("transform", "translate(" + [ point[0] - textBox.width * 0.5 , point[1] - textBox.height - dy - 5 ] + ")"); }else{ path.attr("d", getTooltipBox([textBox.width + dy, textBox.height + dy], "up")); gTooltip .transition() .delay(50) .attr("transform", "translate(" + [ point[0] - textBox.width * 0.5 + 5, point[1] + dy + 5 ] + ")"); } } function onMouseLeave(d){ gTooltip.html(""); } function uniform(s){ s.select(".domain").attr({ fill: "none", stroke: "#999", "stroke-width": 1}); s.selectAll(".tick line").attr({ stroke: "#ddd", "stroke-width": 1 }); s.select("path").remove(); } function getTooltipBox(size, type){ var arrow = 10; if(!type){ type = "down"; } if(type === "up"){ return "M0,0 " + "L" + [0.5 * size[0] - 0.5 * arrow, 0] + " " + "L" + [0.5 * size[0], -0.5 * arrow] + " " + "L" + [0.5 * size[0] + 0.5 * arrow, 0] + " " + "L" + [size[0], 0] + " " + "V" + size[1] + " " + "H0" + "Z"; }else if(type === "down"){ return "M0,0 " + "H" + size[0] + " " + "V" + size[1] + " " + "L" + [0.5 * size[0] + 0.5 * arrow, size[1]] + " " + "L" + [0.5 * size[0], arrow * 0.5 + size[1]] + " " + "L" + [0.5 * size[0] - 0.5 * arrow, size[1]] + " " + "L" + [0, size[1]] + " " + "Z"; } } function getQuantiles(values){ var q1 = d3.quantile(values, 0.25), q2 = d3.median(values), q3 = d3.quantile(values, 0.75), qr = q3 - q1, fValues; fValues = values.filter(function(d){ return d >= (q1 - 1.5 * qr) && d <= (q3 + 1.5 * qr); }); // [Q0, Q1, Q2, Q3, Q4] return [ fValues[0], q1, q2, q3, fValues[fValues.length - 1] ]; } }; // jitterPlot END