a94455c7f118c870b70adca458427e9be168aaad ceisenhart Wed Feb 17 13:47:16 2016 -0800 Adding some new features, making things pretty and some refactoring for speed, refs #16341 diff --git src/hg/js/d3.dendrograms.js src/hg/js/d3.dendrograms.js index 9118264..89d7fc8 100644 --- src/hg/js/d3.dendrograms.js +++ src/hg/js/d3.dendrograms.js @@ -85,32 +85,35 @@ scaleBranchLengths(nodes, w) Scale the branch lengths to correspond to the 'length' field in the json data. addLeafButton(title, count, layer, shift, dropdownSelector, graph) Adds a leaf button to the dropdown skeleton. addNodeButton(dropdownSelector, val, graph, title) Adds a node button to the dropdown skeleton. makeDropdownSkeleton(treeType) Builds a bootstrap dropdown menu skeleton. */ // The color alphabet that was proposed in "A Colour Alphabet and the Limits of Colour Coding" by Paul Green-Armytage 10 August 2010 // The color alphabet capitalizes the number of visibly unqiue colors. var colorAlphabet = ["#015eff", "#fc0a18","#0cc402", "#aea7a5", "#ff15ae", "#d99f07", "#11a5fe", "#037e43", "#ba4455", "#d10aff", "#9354a6", "#7b6d2b", "#08bbbb", "#95b42d", "#b54e04", "#ee74ff", "#2d7593", "#e19772","#fa7fbe", "#fe035b", "#aea0db", "#905e76", "#92b27a", "#03c262"]; + if (!d3) { throw "d3 wasn't included!";} (function(){ + + d3.dendrogram = {}; var colors=d3.scale.category20().range(colorAlphabet); var legendRectSize=10; var legendSpacing=3; var currentLegend=-1; var radius=0; d3.dendrogram.rightAngleDiagonal=function (){ var projection = function(d) { return [d.y, d.x]; }; var path = function(pathData) { return "M" + pathData[0] + ' ' + pathData[1] + " " + pathData[2]; }; function diagonal(diagonalPath, i) { @@ -125,197 +128,174 @@ diagonal.projection = function(x) { if (!arguments.length) return projection; projection = x; return diagonal; }; diagonal.path = function(x) { if (!arguments.length) return path; path = x; return diagonal; }; return diagonal; }; + function componentToHex(c) { + var hex = c.toString(16); + return hex.length == 1 ? "0" + hex : hex; + } + + function rgbToHex(r, g, b) { + return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b); + } + d3.dendrogram.leafColors = function(val, layer, shift,title, selector){ - var vis = d3.select(selector).select("svg"); + + var vis = d3.select(selector).select("svg").select("g").selectAll("g.leaf"); var first = 1; var node = vis.selectAll("g."+ layer + "Leaf"); + //var node = vis.selectAll(layer + "Leaf"); + colors=d3.scale.category20().range(colorAlphabet); node.selectAll("circle") .style("fill", function(d){ // This block links the extra leaf json fields to a specific value (count). This value // is provided when the buttons are written in the main function. if (d.name != " "){ if (val==1) - + { + vis.selectAll("text."+layer+"LegendTitle").remove(); + vis.selectAll("text."+layer+"LegendTitleLabel").remove(); return d3.rgb(d.colorGroup); + } + if (val==-1) + { + vis.selectAll("text."+layer+"LegendTitle").remove(); + vis.selectAll("text."+layer+"LegendTitleLabel").remove(); + return "none"; + } var count = 0; for (var key in d) { if (d.hasOwnProperty(key)){ if (key != "x" && key!="y" && key!= "name" && key!="kids" && key!="length" && key!="colorGroup" && key!="parent" && key!="depth" && key!="rootDist"){ if (count == val){ + var sub = d[key]; + if (typeof d[key] === 'string' && d[key].length > 20) + { + sub = d[key].substring(0,19)+"..."; + } count += 1; - return colors(d[key]); + return colors(sub); } } count += 1; } } } else{return 'none';} }) + .style("stroke-width", ".1px") .attr("transform","translate(" + shift + "," + 0 + ")"); - - var legend=vis.selectAll('g.' + layer + 'Legend').remove(); - updateLegend(colors.domain(), layer, vis, title, shift); + updateLegend(colors.domain(), layer, (d3.select(selector).select("svg.legendSvg").select("g")), title, shift); colors=d3.scale.category20(); }; d3.dendrogram.nodeColors = function(val, selector){ var vis = d3.select(selector).selectAll("svg"); var node=vis.selectAll("g.internalNode").selectAll("circle") .style("fill",function(d){ if (d.name === " "){ if (val==2) return d3.rgb(d.whiteToBlack); if (val==5) return d3.rgb(d.whiteToBlack); if (val==3) return d3.rgb(d.whiteToBlackSqrt); if (val==4) return d3.rgb(d.whiteToBlackQuad); } }) .attr("r", function(d){ if (d.name === " "){ - if (val ==5) return 2; + if (val ==5) return 1; else return 1 + Math.sqrt(d.kids); } }) ; }; updateLegend = function(val, layer, vis, title, shift){ - var legend=vis.selectAll('g.'+layer+'Legend').data(val); - var temp = 200; + // Remove the old legend, title and label. + vis.selectAll('g.' + layer + 'Legend').remove(); + vis.select("text." + layer+ "LegendTitle").remove(); + vis.select("text." + layer+ "LegendLabel").remove(); + + // Make the new legend + var legend=vis.selectAll('g.'+layer+'Legend').data(val); // Define the DOM elements var horz, vert; - var first = 1; - legend.enter() + var first = 1; // Use this as a boolean to only draw the title and label one time. + var viewerWidth = $(document).width(); // Scale to fit width + var viewerHeight = $(document).height(); // Scale to fit height + + legend.enter() // Fill out the DOM elements .append('g') .attr('class',layer+ 'Legend') .attr('transform', function (d, i){ var height=legendRectSize + legendSpacing; var offset= height * colors.domain().length / 2; - if (layer=="inner"){ - horz=(-2 * legendRectSize) + (2*radius) + 45 ; - vert=(i * height - offset) + (radius*(1/2)) + 80; - if (first){ - vis.selectAll("text.innerLegendTitle").remove(); - vis.append("text") - .attr('class','innerLegendTitle') - .style("font-size","16px") - .style("font-weight","bold") - .attr("transform", "translate(" + (horz-45) + "," + (vert-15)+ ")") - .attr('x', legendRectSize + legendSpacing) - .attr('y', legendRectSize - legendSpacing) - .text(function (){ - if (title.length > 15){ - return title.substring(0,14)+"..."; - } - return title; - }); - vis.selectAll("text.outerLegendTitleInner").remove(); - vis.append("text") - .attr('class','outerLegendTitleInner') - .style("font-size","16px") - .style("font-weight","bold") - .attr("transform", "translate(" + (horz-45) + "," + (vert-35)+ ")") - .attr('x', legendRectSize + legendSpacing) - .attr('y', legendRectSize - legendSpacing) - .text("Inner"); - first =0; - } - } - if (layer=="middle"){ - horz=(-2 * legendRectSize) + (2 * radius) + 232 ; - vert=(i * height - offset) + (radius*(1/2)) + 80; - if (first){ - vis.selectAll("text.middleLegendTitle").remove(); - vis.append("text") - .attr('class','middleLegendTitle') - .style("font-size","16px") - .style("font-weight","bold") - .attr("transform", "translate(" + (horz-45) + "," + (vert-15)+ ")") - .attr('x', legendRectSize + legendSpacing) - .attr('y', legendRectSize - legendSpacing) - .text(function (){ - if (title.length > 15){ - return title.substring(0,14)+"..."; - } - return title; - }); - vis.selectAll("text.outerLegendTitleMiddle").remove(); - vis.append("text") - .attr('class','outerLegendTitleMiddle') + vert = (i * height - offset)+ (radius*(1/2)) + 80; // All legends are the same height, the width location varies + // Asign the legend width for each ring, scale to fit. + if (layer=="inner") horz= (viewerWidth*(1/35)); + if (layer=="middle") horz= (viewerWidth*(3/35)); + if (layer=="outer") horz= (viewerWidth*(5/35)); + if (first) + { + vis.append("text")// The layer, this is either 'inner', 'middle', or 'outer'. + .attr('class',layer+'LegendTitle') .style("font-size","16px") .style("font-weight","bold") .attr("transform", "translate(" + (horz-45) + "," + (vert-35)+ ")") .attr('x', legendRectSize + legendSpacing) .attr('y', legendRectSize - legendSpacing) - .text("Middle"); - first =0; - } - } - if (layer=="outer"){ - horz=(-2 * legendRectSize) + (2 * radius) + 420; - vert=(i * height - offset)+ (radius*(1/2) +80 ); - if (first){ - vis.selectAll("text.outerLegendTitle").remove(); - vis.append("text") - .attr('class','outerLegendTitle') + .text(layer); + + vis.append("text")// The title, this is mined from the meta data + .attr('class',layer+'LegendLabel') .style("font-size","16px") .style("font-weight","bold") .attr("transform", "translate(" + (horz-45) + "," + (vert-15)+ ")") .attr('x', legendRectSize + legendSpacing) .attr('y', legendRectSize - legendSpacing) + .text(function (){ if (title.length > 15){ return title.substring(0,14)+"..."; } return title; }); - vis.selectAll("text.outerLegendTitleLabel").remove(); - vis.append("text") - .attr('class','outerLegendTitleLabel') - .style("font-size","16px") - .style("font-weight","bold") - .attr("transform", "translate(" + (horz-45) + "," + (vert-35)+ ")") - .attr('x', legendRectSize + legendSpacing) - .attr('y', legendRectSize - legendSpacing) - .text("Outer"); first =0; } - } - return 'translate(' + horz + ',' + vert + ')'; + return 'translate(' + horz + ',' + vert + ')'; // Actually move the legend. }); + // Apply the colored rectangles. legend.append('rect') .attr('width', legendRectSize) .attr('height', legendRectSize) .style('fill', colors) .style('stroke', colors); + // Give the colored rectangles associated name tags. legend.append('text') .style("font-size","12px") .style("font-weight","bold") .attr('x', legendRectSize + legendSpacing) .attr('y', legendRectSize - legendSpacing) .text(function (d){return d;}); }; scaleBranchLengths = function (nodes, w) { // Visit all nodes and adjust y pos width distance metric var visitPreOrder = function(root, callback) { callback(root); if (root.children) { for (var i = parseInt(root.children.length) - 1; i >= 0; i--){ visitPreOrder(root.children[i], callback); @@ -325,96 +305,101 @@ visitPreOrder(nodes[0], function(node) { node.rootDist = (node.parent ? node.parent.rootDist : 0) + (parseInt(node.length) || 0); }); var rootDists = nodes.map(function(n) { return n.rootDist; }); var yscale = d3.scale.linear() .domain([0, d3.max(rootDists)]) .range([0, w]); visitPreOrder(nodes[0], function(node) { node.y = yscale(node.rootDist); }); return yscale; }; + // The javascript buttons are created at runtime based on the .json file provided. This opens up several cans of worms. + // + // The addLeafButton function is used within this program to write buttons, the buttons themselves use the globally available + // functions d3.dendrogram.leafColors and d3.dendrogram.nodeColors to apply the colors. Note that these buttons exist only when the + // program is being run, they are not present in the basic html. addLeafButton = function (title, count, layer, shift, dropdownSelector, graph) { var leafButton = "<button onclick=\"d3.dendrogram.leafColors("+count+",\'"+layer+"\',"+shift+", \'"+title+"\', \'"+graph+"\')\">"+title+"</button>"; d3.select(dropdownSelector).append("li") .html(leafButton); }; addNodeButton = function (dropdownSelector, val, graph, title) { d3.select(dropdownSelector).append("li").html("<button onclick=\"d3.dendrogram.nodeColors("+val+",'"+graph+"')\">"+title+"</button>"); }; + // This is the first function that is run, it creates the dropdown skeleton which is populated at a later point. makeDropdownSkeleton = function (treeType){ var dropdown = d3.select("#dropdown").append("ul").attr("style","list-style-type:none;display:inline-flex"); d3.select("#dropdown").attr("style","position:fixed;z-index:2"); if (treeType == "Dendro"){ var interiorNodeDropdown = dropdown.append("li").attr("class","dropdown"); interiorNodeDropdown.append("button").attr("class","btn btn-default dropdown-toggle") .attr("type", "button").attr("data-toggle","dropdown").html("Inner "+treeType+" nodes").append("span").attr("class","caret"); interiorNodeDropdown.append("ul").attr("class","dropdown-menu").html("<div id=interiorDendroNodes </div>"); } var innerDropdown = dropdown.append("li").attr("class","dropdown"); innerDropdown.append("button").attr("class","btn btn-default dropdown-toggle") - .attr("type", "button").attr("data-toggle","dropdown").html("Inner "+treeType+" nodes").append("span").attr("class","caret"); + .attr("type", "button").attr("data-toggle","dropdown").html("Inner "+treeType+" leaves").append("span").attr("class","caret"); innerDropdown.append("ul").attr("class","dropdown-menu").html("<div id=inner"+treeType+"Leaves </div>"); var middleDropdown = dropdown.append("li").attr("class","dropdown"); middleDropdown.append("button").attr("class","btn btn-default dropdown-toggle") - .attr("type", "button").attr("data-toggle","dropdown").html("Middle "+treeType+" nodes").append("span").attr("class","caret"); + .attr("type", "button").attr("data-toggle","dropdown").html("Middle "+treeType+" leaves").append("span").attr("class","caret"); middleDropdown.append("ul").attr("class","dropdown-menu").html("<div id=middle"+treeType+"Leaves </div>"); var outerDropdown = dropdown.append("li").attr("class","dropdown"); outerDropdown.append("button").attr("class","btn btn-default dropdown-toggle") - .attr("type", "button").attr("data-toggle","dropdown").html("Outer "+treeType+" nodes").append("span").attr("class","caret"); + .attr("type", "button").attr("data-toggle","dropdown").html("Outer "+treeType+" leaves").append("span").attr("class","caret"); outerDropdown.append("ul").attr("class","dropdown-menu").html("<div id=outer"+treeType+"Leaves </div>"); }; - + // This function generates the cartesian dendrogram (flat). d3.dendrogram.makeCartesianDendrogram =function (selector, data, options) { function zoom() { svgGroup.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")"); } options = options || {}; // define the zoomListener which calls the zoom function on the "zoom" event constrained within the scaleExtents - var viewerWidth = $(document).width(); - var viewerHeight = $(document).height(); + var zoomListener = d3.behavior.zoom().scaleExtent([0.01, 10]) + .on("zoom", zoom); var w = options.width || d3.select(selector).style('width') || d3.select(selector).attr('width'), h = options.height || d3.select(selector).style('height') || d3.select(selector).attr('height'); - + var viewerWidth = $(document).width(); //Scale to fit. + var viewerHeight = $(document).height(); //Scale to fit. var tree = options.tree || d3.layout.cluster() .size([h, w]) .sort(function(node) { return node.children ? parseInt(node.children.length) : -1; }) .children(options.children || function(node) { return node.children; }); - var zoomListener = d3.behavior.zoom().scaleExtent([0.01, 10]) - .on("zoom", zoom); var diagonal = options.diagonal || d3.dendrogram.rightAngleDiagonal(); var vis = options.vis || d3.select(selector) .append("svg") .attr("width", viewerWidth) .attr("height", viewerHeight) .attr("class", "overlay") .call(zoomListener); d3.select(selector).select("svg").append ("rect") .attr("width",w).attr("height",h) .style("fill", "none") .style("stroke","black") .style("stroke-width","5") @@ -580,263 +565,296 @@ .attr("text-anchor", "start") .attr('font-family', 'Helvetica Neue, Helvetica, sans-serif') .attr('font-size', '6px') .attr('fill', 'black') .text(function(d) { return d.name + ' ('+d.length+')'; }); } return {tree: tree, vis: vis}; }; d3.dendrogram.makeRadialDendrogram = function(selector, data, options) { function zoom() { svgGroup.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")"); } + options = options || {}; - radius = (options.radius || 700) / 2; // Global for the legend placement. See function updateLegend. + + var viewerWidth = $(document).width(); // Scale to fit. + var viewerHeight = $(document).height(); // Scale to fit. + + var calc = Math.min(viewerWidth, viewerHeight); + + radius = (calc -50)/ 2; // Global for the legend placement. See function updateLegend. var tree = options.tree || d3.layout.cluster() .size([360, radius - 45]); - var zoomListener = d3.behavior.zoom().scaleExtent([1, 3]) + + var zoomListener = d3.behavior.zoom().scaleExtent([1, 6]) .on("zoom", zoom); var diagonal=d3.svg.diagonal.radial() .projection(function(d){return [d.y, d.x / 180 * Math.PI];}); - var width = (radius * 2) + 570, - height = (radius * 2) + 50; + var width = viewerWidth -25, + height = viewerHeight - 50; + + var metaHorz = (viewerWidth)/8, + metaVert = 6*(viewerHeight)/8; var vis = options.vis || d3.select(selector) .append("svg") - .attr("width", width) + .attr("width", 3*width/5 ) .attr("height", height) .attr("class", "overlay") .call(zoomListener); + var legendVis = options.vis || d3.select(selector) + .append("svg") + .attr("class", "legendSvg") + .attr("width", 2*width/5) + .attr("height", height); + vis.append ("rect") - .attr("width",width).attr("height",height) + .attr("width", 3*width/5) + .attr("height", height) + .style("fill", "none") + .style("stroke","black") + .style("stroke-width","5") + .style("opacity","0.5"); + + legendVis.append ("rect") + .attr("width", 2*width/5) + .attr("height", height) .style("fill", "none") .style("stroke","black") .style("stroke-width","5") .style("opacity","0.5"); + legendVis.append("g"); + var svgGroup = vis.append("g") .attr("transform","translate(" + (radius) + "," + radius + ")"); d3.select("svg").on("wheel.zoom",null); d3.select("svg").on("mousewheel.zoom",null); var tooltip = d3.select(selector) .append('div') .attr('class', 'tooltip') .style("opacity",0); var legendRectSize=20; var legendSpacing=4; var currentLegend=-1; var nodes=tree.nodes(data); var link=svgGroup.selectAll("path.link") .data(tree.links(nodes)).enter() .append("path") .attr("class", "link") .attr("fill", "none") - .attr("stroke", "#aaa") - .attr("stroke-width", "1px") + .attr("stroke", "black") + .attr("stroke-width", ".1px") .attr("d", diagonal); makeDropdownSkeleton("Dendro"); //Looks for the div 'dropdown' and generates the outer list and buttons // For the radial display the interior node color options are constant (calculated on the backend in the C code) - addLeafButton("distance", 1, 'inner', 15, '#innerDendroLeaves', '#dendrogram'); - addLeafButton("distance", 1, 'middle', 25, '#middleDendroLeaves', '#dendrogram'); - addLeafButton("distance", 1, 'outer', 35, '#outerDendroLeaves', '#dendrogram') ; + addLeafButton("Remove ring", -1, 'inner', 10, '#innerDendroLeaves', '#dendrogram'); + addLeafButton("Remove ring", -1, 'middle', 15, '#middleDendroLeaves', '#dendrogram'); + addLeafButton("Remove ring", -1, 'outer', 20, '#outerDendroLeaves', '#dendrogram') ; + addLeafButton("Distance", 1, 'inner', 10, '#innerDendroLeaves', '#dendrogram'); + addLeafButton("Distance", 1, 'middle', 15, '#middleDendroLeaves', '#dendrogram'); + addLeafButton("Distance", 1, 'outer', 20, '#outerDendroLeaves', '#dendrogram') ; addNodeButton("#interiorDendroNodes" ,2, selector, "Tpm distance"); addNodeButton("#interiorDendroNodes" ,3, selector, "Square root tpm distance"); addNodeButton("#interiorDendroNodes" ,4, selector, "Quad root tpm distance"); addNodeButton("#interiorDendroNodes" ,5, selector, "Make nodes smaller"); var first = 1; - var innerNodes = svgGroup.selectAll("g") + + // These are true nodes in the sense that they are actually calculated and defined, + // however this code is not repsonsible for the actual visuale representation. This + // block only handles the backend DOM construction, the shapes and colors are assigned later. + var trueNodes = svgGroup.selectAll("g") .data(nodes).enter() .append("g") .attr("class", function(n) { if (n.children) { return "internalNode"; } else{ if (first){ var count = 0; for (var key in n) { if (n.hasOwnProperty(key)) { + // Give each dropdown button a bunch of options. if (key != "x" && key!="y" && key!= "name" && key!="kids" && key!="length" && key!="colorGroup" && key!="parent" && key!="depth" && key!="rootDist"){ - addLeafButton(key, count, 'inner', 15, '#innerDendroLeaves', '#dendrogram'); - addLeafButton(key, count, 'middle', 25, '#middleDendroLeaves', '#dendrogram'); - addLeafButton(key, count, 'outer', 35, '#outerDendroLeaves', '#dendrogram') ; + addLeafButton(key, count, 'inner', 10, '#innerDendroLeaves', '#dendrogram'); + addLeafButton(key, count, 'middle', 15, '#middleDendroLeaves', '#dendrogram'); + addLeafButton(key, count, 'outer', 20, '#outerDendroLeaves', '#dendrogram') ; } } count += 1; } first = 0; } - return "g.innerLeaf"; + return "leaf"; } }) .attr("transform", function(d){return "rotate(" + (d.x - 90) + ")translate(" + d.y + ")";}); - var temp = 1; - var temp2; + + + + // This section handles the internal node visual representation, including the node initial size/color + // and the tooltips (on click visualizations) + var filled = 1; + var prevSel, prevSel2; var interiorNodes = svgGroup.selectAll("g.internalNode") .append("circle") - .attr("r", function(d){ + .attr("r", function(d){ // Handle node size if (d.name==" "){ return 1 + Math.sqrt(d.kids);} else{return 0;} }) - .style("fill", function(d){ + .style("fill", function(d){ // Handle node color if (d.name==" "){return d.whiteToBlack;} else{return "none";}}) - .on("click", function(d) { + .on("click", function(d) { // Tooltip stuffs d3.select(this).style("fill", "red"); - if (temp ==2) { - console.log(temp2); - temp2.style("fill", d.whiteToBlack); - for (var temp3 in d){console.log(temp3);} + if (filled == 2) { + if (prevSel2.name==" "){prevSel.style("fill", prevSel2.whiteToBlack);} + else{prevSel.style("fill", "white");} } tooltip.transition() .duration(200) .style("opacity", 0.9); - result = "<table border=\"1\">"; //"Contributing genes" + "</td><td>" +d.contGenes + "td/>"; + result = "<table border=\"1\">"; // Make the tooltip a table, the first section is the basic meta data count = 1; - result = result +"<tr><td colspan=\"4\" style=\"font-size:14px;text-align:center\">Internal node meta data</td>"; - //result = result +"<td></td><td></td></tr>"; + result = result +"<tr><td colspan=\"4\" style=\"font-weight:bold;font-size:14px;text-align:center\">Node meta data</td>"; for (var key in d) { if (d.hasOwnProperty(key)) { if (key == "number" || key == "rootDist" || key == "kids" || key == "tpmDistance" || key == "length" || key == "contGenes" || key == "normalizedDistance" || key == "depth") { if (count == 1){ count = 2; - result = result +"<tr><td style=\"font-size:10px;\">" + key + "</td><td style=\"font-size:10px;\">" + d[key] + "</td>"; + result = result +"<tr><td style=\"font-weight:bold;font-size:10px;\">" + key + "</td><td style=\"font-weight:bold;font-size:10px;\">" + d[key] + "</td>"; } else{ count = 1; - result = result +"<td style=\"font-size:10px;\">" + key + "</td><td style=\"font-size:10px;\">" + d[key] + "</td></tr>"; + result = result +"<td style=\"font-weight:bold;font-size:10px;\">" + key + "</td><td style=\"font-weight:bold;font-size:10px;\">" + d[key] + "</td></tr>"; } } } } count = 1; - result = result +"<tr><td colspan=\"2\" style=\"font-size:14px\">Top 10 contributing genes </td><td colspan=\"2\" style=\"font-size:14px\">TPM value contributed</td>"; - //result = result +"<td></td><td></td></tr>"; + // This section handles the tooltip representation for the top contributing genes. + result = result +"<tr><td colspan=\"2\" style=\"font-weight:bold;font-size:14px\">Top 10 contributing genes </td><td colspan=\"2\" style=\"font-weight:bold;font-size:14px\">TPM value contributed</td>"; for (var n in d.geneList){ - //result = result + n + " " + d.geneList[n] + "<br/>"; if (count == 1){ count = 2; - result = result +"<tr><td style=\"font-size:10px\">" + n + "</td><td style=\"font-size:10px\">" + d.geneList[n] + "</td>"; + result = result +"<tr><td style=\"font-weight:bold;font-size:10px\">" + n + "</td><td style=\"font-weight:bold;font-size:10px\">" + d.geneList[n] + "</td>"; } else{ count = 1; - result = result +"<td style=\"font-size:10px\">" + n + "</td><td style=\"font-size:10px\">" + d.geneList[n] + "</td></tr>"; + result = result +"<td style=\"font-weight:bold;font-size:10px\">" + n + "</td><td style=\"font-weight:bold;font-size:10px\">" + d.geneList[n] + "</td></tr>"; } } result = result + "</table"; tooltip.html(result) - .style("right", "160px") - .style("top", "500px"); - temp = 2; - temp2 = d3.select(this); + .style("right", metaHorz + "px") + .style("top", metaVert + "px"); + filled = 2; + prevSel2 = d; + prevSel = d3.select(this); }) .style("stroke", "black") - .style("stroke-width", ".25px"); + .style("stroke-width", ".1px"); - var trueLeaves = svgGroup.selectAll("g.trueLeaf") - .data(nodes).enter() + var leaves = svgGroup.selectAll("g.leaf"); + + leaves .append("g") .attr("class", "trueLeaf") - .attr("transform",function(d){return "rotate(" + (d.x - 90) + ")translate(" + d.y + ")";}); - - var temp4 =1; - var temp5; - trueLeaves.append("circle") + .append("circle") .attr("r", function(d){ - if (d.name !==" "){return 2;}}) + if (d.name !==" "){return 1;}}) .style("fill", function(d){ - if (d.name !==" "){return "d.whiteToBlack";} + if (d.name ==" "){return "d.whiteToBlack";} + else { return "white";} }) - .attr("transform","translate(" + 5 + "," + 0 + ")") + .style("stroke", "black") + .style("stroke-width", ".1px") .on("click", function(d) { d3.select(this).style("fill", "red"); - if (temp4 ==2) { - console.log(temp5); - temp5.style("fill", d.whiteToBlack); - for (var temp3 in d){console.log(temp3);} + if (filled == 2) + { + if (prevSel2.name==" "){prevSel.style("fill", prevSel2.whiteToBlack);} + else{prevSel.style("fill", "white");} } tooltip.transition() .duration(200) .style("opacity", 0.9); result = "<table border=\"1\">"; count = 1; - result = result +"<tr><td colspan=\"4\" style=\"font-size:14px;text-align:center\">Inner leaf meta data</td>"; + result = result +"<tr><td colspan=\"4\" style=\"font-weight:bold;font-size:14px;text-align:center\">Leaf meta data</td>"; for (var key in d) { if (d.hasOwnProperty(key)) { if (key != "x" && key!="y" && key!="kids" && key!="colorGroup" && key!="parent") { var sub1 = key, sub2 = d[key]; if (typeof key === 'string' && key.length > 20) { sub1 = key.substring(0,19)+"..."; } if (typeof d[key] === 'string' && d[key].length > 20) { sub2 = d[key].substring(0,19)+"..."; } - console.log(key, sub1, d[key],sub2); if (count == 1){ count = 2; - result = result +"<tr><td style=\"font-size:10px;\">" + sub1 + "</td><td style=\"font-size:10px;\">" + sub2 + "</td>"; + result = result +"<tr><td style=\"font-weight:bold;font-size:10px;\">" + sub1 + "</td><td style=\"font-weight:bold;font-size:10px;\">" + sub2 + "</td>"; } else{ count = 1; - result = result +"<td style=\"font-size:10px;\">" + sub1 + "</td><td style=\"font-size:10px;\">" + sub2 + "</td></tr>"; + result = result +"<td style=\"font-weight:bold;font-size:10px;\">" + sub1 + "</td><td style=\"font-weight:bold;font-size:10px;\">" + sub2 + "</td></tr>"; } } } } result = result + "</table"; tooltip.html(result) - .style("right", "100px") - .style("top", "500px"); - temp4 = 2; - temp5 = d3.select(this); + .style("right", metaHorz + "px") + .style("top", metaVert + "px"); + filled = 2; + prevSel = d3.select(this); + prevSel2 = d; }); - var innerLeaves = svgGroup.selectAll("g.innerLeaf") - .data(nodes).enter() + leaves .append("g") - .attr("class", "innerLeaf") - .attr("transform",function(d){return "rotate(" + (d.x - 90) + ")translate(" + d.y + ")";}) + .attr("class", "outerLeaf") .append("circle") - .attr("r",4) + .attr("r", 2) .style("fill","none"); - var middleLeaves = svgGroup.selectAll("g.middleLeaf") - .data(nodes).enter() + leaves .append("g") .attr("class", "middleLeaf") - .attr("transform",function(d){return "rotate(" + (d.x - 90) + ")translate(" + d.y + ")";}) .append("circle") - .attr("r", 4) + .attr("r", 2) .style("fill","none"); - var outerLeaves = svgGroup.selectAll("g.outerLeaf") - .data(nodes).enter() + leaves .append("g") - .attr("class", "outerLeaf") - .attr("transform",function(d){return "rotate(" + (d.x - 90) + ")translate(" + d.y + ")";}) + .attr("class", "innerLeaf") .append("circle") - .attr("r", 4) + .attr("r",2) .style("fill","none"); + return {tree: tree, vis: svgGroup}; }; })();