fabsta
8/24/2013 - 9:32 PM

index.html

<!DOCTYPE html>
<meta charset="utf-8">
<title>Hive Plots</title>
<style>

@import url(../style.css?20120427);

.axis {
  stroke: #000;
  stroke-width: 1.5px;
}

.node circle {
  stroke: #000;
}

.link {
  fill: none;
  stroke: #999;
  stroke-width: 1.5px;
  stroke-opacity: .3;
}

.link.active {
  stroke: red;
  stroke-width: 2px;
  stroke-opacity: 1;
}

.link.ortholog {
  stroke: green;
  stroke-width: 2px;
  stroke-opacity: 1;
}


.node circle.active {
  stroke: red;
  stroke-width: 3px;
}

</style>


<h1>TreeFam Homology Hive Plots</h1>

<p>Mouseover to show the homology relationships for the corresponding gene.
  <p id="info">Loading…

<p id="chart">


<p style="margin-top:4em;">Special thanks to <a href="http://www.cfcl.com/rdm/">Rich Morin</a> for assisting in the implementation of hive plots in D3!

<footer>
  <aside>March 18, 2012</aside>
  <a href="../" rel="author">Mike Bostock</a>
</footer>

<script src="http://d3js.org/d3.v2.js?2.8.1"></script>
<script>

var width = 960,
    height = 850,
    innerRadius = 40,
    outerRadius = 640,
    majorAngle = 2 * Math.PI / 3,
    minorAngle = 1 * Math.PI / 12;

var angle = d3.scale.ordinal()
    //.domain(["source", "source-target", "target-source", "target"])
    .domain(["source", "source-target", "target-source", "target"])
//		.range([0, majorAngle - minorAngle, majorAngle + minorAngle, 2 * majorAngle])
		.range([0, majorAngle - minorAngle, majorAngle + minorAngle, 2 * majorAngle]);

var radius = d3.scale.linear()
    .range([innerRadius, outerRadius]);

var color = d3.scale.category10();

var svg = d3.select("#chart").append("svg")
    .attr("width", width)
    .attr("height", height)
  .append("g")
    //.attr("transform", "translate(" + outerRadius * .20 + "," + outerRadius * .57 + ")")
	.attr("transform", "translate(140,40)")
	;

// Load the data and display the plot!
d3.json("treefam.json", function(nodes) {
  var nodesByName = {},
  	  genesByName = {},
      links = [],
	  geneNodes = [],
	  homologyNodes = [],
	  ignoreSpecies = {},
	  ignoreRank = {},
      formatNumber = d3.format(",d"),
      defaultInfo;
	  image_path = "../../lib/images/";
	  var rank_x = 200;
	  var species_x = 400;
	  var homologs_x = 600;
	  var gene_distance = 12;
	  var rank_distance = 12;
	  var species_distance = 12;
	  var homologs_distance = 12;
	  var rankNodes = [];
	  var speciesNodes = [];
	  

	  // iterating over all input genes
	  // Construct an index by node name.
	  nodes.forEach(function(d) {
	    d.connectors = [];
	    d.packageName = d.name.split(".")[0];
	    genesByName[d.name] = d;
	  });
	  //console.log(genesByName);
	  
	  
	  // Convert the import lists into links with sources and targets.
	  nodes.forEach(function(source) {
		//	console.log(source);
		//console.log(source.imports);
	    
		source.imports.forEach(function(targetName) {
		 // console.log(targetName);
	      var target = genesByName[targetName];
		  //console.log("adding to links: "+links.length);
		  //if(!target){console.log("could not find target for "+targetName.target);}
	      //if (!source.source) {
			//  console.log("source: add to connectors");
			//  source.connectors.push(source.source = {node: source, degree: 0});
		  //}
	      //if (!target.target){ 
			//console.log("target: add to connectors");
			 // target.connectors.push(target.target = {node: target, degree: 0});
		  //}
		  //rank2species.push(d.rank);
	      links.push({source: source, target: targetName});
		  if( ignoreRank[targetName.rank] === undefined ) {
		  	rankNodes.push(targetName);
			ignoreRank[targetName.rank] = 1;
		  }
		  if( ignoreSpecies[targetName.taxon] === undefined ) {
		  	speciesNodes.push(targetName);
			ignoreSpecies[targetName.taxon] = 1;
		  }	
		 // if( ignoreS[targetName.taxon] === undefined ) {
		  	homologyNodes.push(targetName);
			//ignoreHomology[targetName.taxon] = 1;
		  //}	
		  
		  	  
		  
	    });
		geneNodes.push(source);
	  });
	  console.log("species");
	  console.log(speciesNodes);
	  //console.log(nodes);
	  
	  //test
	 // console.log("geneNodes");
	 // console.log(geneNodes);
	 // console.log("homologyNodes");
	 // console.log(homologyNodes);
	  
	  
//	  test
	  nodes.forEach(function(node) {
	    if (node.source && node.target) {
	      node.type = node.source.type = "target-source";
	      node.target.type = "source-target";
	    } else if (node.source) {
	      node.type = node.source.type = "source";
	    } else if (node.target) {
	      node.type = node.target.type = "target";
	    } else {
	      node.homologs = [{node: node}];
	      node.type = "source";
	    }
	  });
	  //console.log(nodes);
	  //var 
	  
	  // Nest nodes by type, for computing the rank.
	  var nodesByType = d3.nest()
	      .key(function(d) { return d.type; })
	      .sortKeys(d3.ascending)
	      .entries(nodes);

	  // Duplicate the target-source axis as source-target.
	 // nodesByType.push({key: "source-target", values: nodesByType[2].values});

	  // Compute the rank for each type, with padding between packages.
	  nodesByType.forEach(function(type) {
	    var lastName = type.values[0].packageName, count = 0;
	    type.values.forEach(function(d, i) {
	      if (d.packageName != lastName) lastName = d.packageName, count += 2;
	      d.index = count++;
	    });
	    type.count = count - 1;
	  });
	  var specieslookup = {};
	  var species_counter = 0;
	  	  homologyNodes.forEach(function(d) {
			  //console.log(d.name);
			  if( specieslookup[d.name] === undefined ) {
				//  console.log("not found yet");
				  specieslookup[d.name] = species_counter++;
			  		d.index = specieslookup[d.name];
					console.log(d.index);
			  }else{
				//  console.log("found!");
		  		d.index = specieslookup[d.name];
			  }
			//console.log(d.index);
		  });
		  
		  console.log(specieslookup);
		  
		 // dsfsdf
	 /* svg.selectAll(".axis")
	      .data(nodesByType)
	    .enter().append("line")
	      .attr("class", "axis")
	      .attr("transform", function(d) { return "rotate(" + degrees(angle(d.key)) + ")"; })
	      .attr("x1", radius(-2))
	      .attr("x2", function(d) { return radius(d.count + 2); });
	  */
	 
	 var lineFunction = d3.svg.line()
	                           .x(function(d) { return d.x; })
	                           .y(function(d) { return d.y; })
	                          .interpolate("basis");
							  console.log(links.length+" links found");
							  console.log(links);
			//				  dffdf
// Draw the links.
	  svg.append("g")
	      .attr("class", "links")
	    .selectAll(".link")
	      .data(links)
	    .enter().append("path")
	      .attr("class", "link")
		  .attr("d", function(d){
			  //console.log(d.source);
			  //console.log(d.target);
			  var source_name = d.source.name;
			  var target_name = d.target.name;
			  var source_index = d.source.index;
			  var target_index = specieslookup[d.target.name];
			  console.log("source: "+source_name+" "+source_index+" and target: "+target_name+" "+target_index);
			  var source_x = 0 ;
			  var source_y = 50+ (source_index*7);
			  var target_x = 700 ;
			  var target_y = 50 + (target_index*20);
			  console.log(""+d.source.name+" has "+source_x+"y"+source_y+"x"+target_x+"y"+target_y);
			  var lineData = [ { "x": source_x,   "y": source_y},  { "x": target_x,  "y": target_y}];
			  return lineFunction(lineData);
	    	  //return "M10,25L10,75L60,75L10,25";
			}
	  		)
	      .on("mouseover", linkMouseover)
	      .on("mouseout", mouseout);
	  
		  //looool
		  var silent_counter=0;

// Draw gene nodes	  

	  var all_gene_nodes = svg.append("g")
	      .attr("class", "nodes")
	    .selectAll(".node")
	      .data(geneNodes)
	    .enter().append("g")
	      .attr("class", "node")
	      .style("fill", function(d) { 
			  console.log("color: for "+d.name+" and package: "+d.packageName+" is "+color(d.packageName));
			  return color(d.packageName); 
		  })
	      .on("mouseover", nodeMouseover)
	      .on("mouseout", mouseout);  
		  
	  /*  var all_gene_circles = all_gene_nodes.selectAll("circle")
	      .data(function(d) { return d.homologs; })
	      .enter().append("circle")
		  .attr("cy", function(d,i) { 
				console.log("node index : "+i+" "+(40 + (silent_counter++ * 10)));
				return 40 + (silent_counter++ * 10); 
		  })
		  .attr("cx", function(d){
			  if(d.type = "source"){return 0;}
			  else{ return 30;}
		  })
	      .attr("r", 4);
		*/  
		 var all_gene_circles = all_gene_nodes.append("svg:image")
		              //.attr("y", -10)
					  .attr("y", function(d,i) { 
							console.log("node index : "+i+" "+(40 + (silent_counter++ * 10)));
							return 30 + (silent_counter++ * gene_distance); 
					  })
					  .attr("x", function(d){
						  if(d.type = "source"){return 0;}
						  else{ return 30;}
					  })
		              .attr("text-anchor", function(d){ return  "end";})
		              .attr("width", 12).attr("height", 12)
		              //.attr("xlink:href", function(d) { return d.children == null? image_path+"/thumb_"+d.taxon+".png" : "";  });
		          .attr("xlink:href", function(d) { return "../data/MB_dna.gif";  });
		  
		  
		  
		  all_gene_nodes.insert("svg:text")
		              .attr("x", -8)
		              .attr("y", function(d,i){ return 55 + (i * gene_distance*2);})
		              //.attr("class","innerNode_label")
					  .attr("font-size", "15")
		              .attr("text-anchor", function(d){ return "end";})
					  .text(function(d) { return d.name;});
		  
	silent_counter = 0;
	
	
// draw rank nodes	
	var all_rank_nodes = svg.append("g")
      .attr("class", "nodes")
    .selectAll(".node")
      .data(rankNodes)
    .enter().append("g")
      .attr("class", "node")
      .style("fill", function(d) { 
		  console.log("color: for "+d.name+" and package: "+d.rank+" is "+color(d.rank));
		  return color(d.rank); 
	  })
      .on("mouseover", nodeMouseover)
      .on("mouseout", mouseout);  
	  
    var all_rank_circles = all_rank_nodes
	  .append("circle")
	  .attr("cy", function(d,i) { 
			console.log("node index : "+i+" "+(40 + (silent_counter++ * rank_distance)));
			return 40 + (silent_counter++ * 10); 
	  })
	  .attr("cx", function(d){
		  return rank_x;
	  })
      .attr("r", 4);
	  
	  all_rank_nodes.insert("svg:text")
	              .attr("x", rank_x + 10)
	              .attr("y", function(d,i){ return 55 + (i * rank_distance * 2);})
	              //.attr("class","innerNode_label")
				  .attr("font-size", "15")
	              .attr("text-anchor", function(d){ return  "start";})
				  .text(function(d) { return d.rank;});
	
   silent_counter = 0;
	
  var all_species_nodes = svg.append("g")
      .attr("class", "nodes")
    .selectAll(".node")
      .data(speciesNodes)
    .enter().append("g")
      .attr("class", "node")
      .style("fill", function(d) { 
		  console.log("color: for "+d.name+" and package: "+d.rank+" is "+color(d.rank));
		  return color(d.rank); 
	  })
      .on("mouseover", nodeMouseover)
      .on("mouseout", mouseout);  
	  
	 var all_species_circles = all_species_nodes.append("svg:image")
	              //.attr("y", -10)
				  .attr("y", function(d,i) { 
						console.log("node index : "+i+" "+(40 + (silent_counter++ * species_distance)));
						return 30 + (silent_counter++ * 10); 
				  })
				  .attr("x", function(d){
					  if(d.type = "source"){return species_x;}
					  else{ return 30;}
				  })
	              
	              .attr("text-anchor", function(d){ return  "end";})
	              .attr("width", 15).attr("height", 15)
	              //.attr("xlink:href", function(d) { return d.children == null? image_path+"/thumb_"+d.taxon+".png" : "";  });
	          .attr("xlink:href", function(d) { return image_path+"/thumb_"+d.taxon+".png";  });

	  
	  all_species_nodes.insert("svg:text")
	              .attr("x", species_x + 10)
	              .attr("y", function(d,i){ return 55 + (i * species_distance * 2);})
	              //.attr("class","innerNode_label")
				  .attr("font-size", "15")
	              .attr("text-anchor", function(d){ return  "start";})
				  .text(function(d) { return d.taxon;});
	
	/*var all_homology_nodes = svg.append("g")
				      .attr("class", "nodes")
				    .selectAll(".node")
				      .data(homologyNodes)
				    .enter().append("g")
				      .attr("class", "node")
				      .style("fill", function(d) { 
						  console.log("color: for "+d.name+" and package: "+d.rank+" is "+color(d.rank));
						  return color(d.rank); 
					  })
				      .on("mouseover", nodeMouseover)
				      .on("mouseout", mouseout);  
	  
		var all_homology_circles = all_homology_nodes.append("svg:image")
 		              //.attr("y", -10)
 					  .attr("y", function(d,i) { 
 							console.log("node index : "+i+" "+(40 + (silent_counter++ * homologs_distance)));
 							return 30 + (silent_counter++ * homologs_distance); 
 					  })
 					  .attr("x", function(d){
 						  if(d.type = "source"){return homologs_x;}
 						  else{ return 30;}
 					  })
 		              .attr("text-anchor", function(d){ return  "end";})
 		              .attr("width", 15).attr("height", 15)
 		              //.attr("xlink:href", function(d) { return d.children == null? image_path+"/thumb_"+d.taxon+".png" : "";  });
 		          .attr("xlink:href", function(d) { return "../data/MB_dna.gif";  });
		  
					  
	  
					  all_species_nodes.insert("svg:text")
					              .attr("x", homologs_x + 10)
					              .attr("y", function(d,i){ return 55 + (i * homologs_distance * 2);})
					              //.attr("class","innerNode_label")
								  .attr("font-size", "15")
					              .attr("text-anchor", function(d){ return  "start";})
								  .text(function(d) { return d.name;});
	*/
	
	
	
		  // Initialize the info display.
		  var info = d3.select("#info")
		      .text(defaultInfo = "Showing " + formatNumber(links.length) + " homologies for " + formatNumber(nodes.length) + " genes.");
	
	  
		  /*
		 // test
  // Construct an index by node name.
  nodes.forEach(function(d) {
    d.connectors = [];
    d.packageName = d.name.split(".")[1];
    nodesByName[d.name] = d;
  });

  // Convert the import lists into links with sources and targets.
  nodes.forEach(function(source) {
    source.imports.forEach(function(targetName) {
      var target = nodesByName[targetName];
      if (!source.source) source.connectors.push(source.source = {node: source, degree: 0});
      if (!target.target) target.connectors.push(target.target = {node: target, degree: 0});
      links.push({source: source.source, target: target.target});
    });
  });

  // Determine the type of each node, based on incoming and outgoing links.
  nodes.forEach(function(node) {
    if (node.source && node.target) {
     // node.type = node.source.type = "target-source";
      node.target.type = "source-target";
    } else if (node.source) {
      node.type = node.source.type = "source";
    } else if (node.target) {
      node.type = node.target.type = "target";
    } else {
      node.connectors = [{node: node}];
      node.type = "source";
    }
  });


  // Normally, Hive Plots sort nodes by degree along each axis. However, since
  // this example visualizes a package hierarchy, we get more interesting
  // results if we group nodes by package. We don't need to sort explicitly
  // because the data file is already sorted by class name.

  // Nest nodes by type, for computing the rank.
  var nodesByType = d3.nest()
      .key(function(d) { return d.type; })
      .sortKeys(d3.ascending)
      .entries(nodes);

  // Duplicate the target-source axis as source-target.
  nodesByType.push({key: "source-target", values: nodesByType[2].values});

  // Compute the rank for each type, with padding between packages.
  nodesByType.forEach(function(type) {
    var lastName = type.values[0].packageName, count = 0;
    type.values.forEach(function(d, i) {
      if (d.packageName != lastName) lastName = d.packageName, count += 2;
      d.index = count++;
    });
    type.count = count - 1;
  });

  // Set the radius domain.
  radius.domain(d3.extent(nodes, function(d) { return d.index; }));

  // Draw the axes.
  svg.selectAll(".axis")
      .data(nodesByType)
    .enter().append("line")
      .attr("class", "axis")
      .attr("transform", function(d) { return "rotate(" + degrees(angle(d.key)) + ")"; })
      .attr("x1", radius(-2))
      .attr("x2", function(d) { return radius(d.count + 2); });

  // Draw the links.
  svg.append("g")
      .attr("class", "links")
    .selectAll(".link")
      .data(links)
    .enter().append("path")
      .attr("class", "link")
      .attr("d", link()
      .angle(function(d) { return angle(d.type); })
      .radius(function(d) { return radius(d.node.index); }))
      .on("mouseover", linkMouseover)
      .on("mouseout", mouseout);

  // Draw the nodes. Note that each node can have up to two connectors,
  // representing the source (outgoing) and target (incoming) links.
  svg.append("g")
      .attr("class", "nodes")
    .selectAll(".node")
      .data(nodes)
    .enter().append("g")
      .attr("class", "node")
      .style("fill", function(d) { return color(d.packageName); })
    .selectAll("circle")
      .data(function(d) { return d.connectors; })
    .enter().append("circle")
      .attr("transform", function(d) { 
	console.log("rotate "+d.type+"(" + degrees(angle(d.type)) + ")");
	
	return "rotate(" + degrees(angle(d.type)) + ")"; })
      
	.attr("cx", function(d) { 
	console.log("node index : "+d.node.index+" "+radius(d.node.index));
	return radius(d.node.index); 
	
	return radius(d.node.index); })
      .attr("r", 4)
      .on("mouseover", nodeMouseover)
      .on("mouseout", mouseout);
*/
  // Highlight the link and connected nodes on mouseover.
  function linkMouseover(d) {
	//console.log(d);
	console.log(d.source.name);
    svg.selectAll(".link").classed("active", function(p) { 
		//console.log(d.name+" == "+p.name);
		//console.log(p);
		//console.log(p.source.name);
		//console.log("");
		
		return p.source.name === d.source.name; 
	});
    svg.selectAll(".node circle").classed("active", function(p) { return p === d.source || p === d.target; });
	console.log(d.source.name + " → " + d.target.name);
    info.text(d.source.name + " → " + d.target.name);
  }

  // Highlight the node and connected links on mouseover.
  function nodeMouseover(d) {
    var all_nodes = svg.selectAll(".link").classed("active", function(p) { return p.source.name === d.name || p.target.name === d.name; });
     d3.select(this).classed("active", true);
    info.text(d.name);
  }

  // Clear any highlighted nodes or links.
  function mouseout() {
    svg.selectAll(".active").classed("active", false);
    info.text(defaultInfo);
  } 
});

// A shape generator for Hive links, based on a source and a target.
// The source and target are defined in polar coordinates (angle and radius).
// Ratio links can also be drawn by using a startRadius and endRadius.
// This class is modeled after d3.svg.chord.
function link() {
  var source = function(d) { return d.source; },
      target = function(d) { return d.target; },
      angle = function(d) { return d.angle; },
      startRadius = function(d) { return d.radius; },
      endRadius = startRadius,
      arcOffset = -Math.PI / 2;

	  console.log("now we are in link with "+source+" and "+target+"");

  function link(d, i) {
    var s = node(source, this, d, i),
        t = node(target, this, d, i),
        x;
    if (t.a < s.a) x = t, t = s, s = x;
    if (t.a - s.a > Math.PI) s.a += 2 * Math.PI;
    var a1 = s.a + (t.a - s.a) / 3,
        a2 = t.a - (t.a - s.a) / 3;
    return s.r0 - s.r1 || t.r0 - t.r1
        ? "M" + Math.cos(s.a) * s.r0 + "," + Math.sin(s.a) * s.r0
        + "L" + Math.cos(s.a) * s.r1 + "," + Math.sin(s.a) * s.r1
        + "C" + Math.cos(a1) * s.r1 + "," + Math.sin(a1) * s.r1
        + " " + Math.cos(a2) * t.r1 + "," + Math.sin(a2) * t.r1
        + " " + Math.cos(t.a) * t.r1 + "," + Math.sin(t.a) * t.r1
        + "L" + Math.cos(t.a) * t.r0 + "," + Math.sin(t.a) * t.r0
        + "C" + Math.cos(a2) * t.r0 + "," + Math.sin(a2) * t.r0
        + " " + Math.cos(a1) * s.r0 + "," + Math.sin(a1) * s.r0
        + " " + Math.cos(s.a) * s.r0 + "," + Math.sin(s.a) * s.r0
        : "M" + Math.cos(s.a) * s.r0 + "," + Math.sin(s.a) * s.r0
        + "C" + Math.cos(a1) * s.r1 + "," + Math.sin(a1) * s.r1
        + " " + Math.cos(a2) * t.r1 + "," + Math.sin(a2) * t.r1
        + " " + Math.cos(t.a) * t.r1 + "," + Math.sin(t.a) * t.r1;
  }

  function node(method, thiz, d, i) {
    var node = method.call(thiz, d, i),
        a = +(typeof angle === "function" ? angle.call(thiz, node, i) : angle) + arcOffset,
        r0 = +(typeof startRadius === "function" ? startRadius.call(thiz, node, i) : startRadius),
        r1 = (startRadius === endRadius ? r0 : +(typeof endRadius === "function" ? endRadius.call(thiz, node, i) : endRadius));
    return {r0: r0, r1: r1, a: a};
  }

  link.source = function(_) {
    if (!arguments.length) return source;
    source = _;
    return link;
  };

  link.target = function(_) {
    if (!arguments.length) return target;
    target = _;
    return link;
  };

  link.angle = function(_) {
    if (!arguments.length) return angle;
    angle = _;
    return link;
  };

  link.radius = function(_) {
    if (!arguments.length) return startRadius;
    startRadius = endRadius = _;
    return link;
  };

  link.startRadius = function(_) {
    if (!arguments.length) return startRadius;
    startRadius = _;
    return link;
  };

  link.endRadius = function(_) {
    if (!arguments.length) return endRadius;
    endRadius = _;
    return link;
  };

  return link;
}

function degrees(radians) {
  return radians / Math.PI * 180 - 90;
}

</script>

[
{"name":"BRCA2_HUMAN", "taxon":"Homo_sapiens", "rank":"primates","size":3938,"imports":[
  				{"type":"inparalog","name": "BRCA1_HUMAN","target":"primates.Human","taxon" : "Homo_sapiens", "rank":"primates"},
					{"type":"ortholog","name": "BRCA2_MOUSE","target":"glires.Mouse","taxon" : "Mus_musculus", "rank":"glires"}
					]},
{"name":"BRCA2_RAT", "taxon":"Rattus_norvegicus", "rank":"glires","size":19975,"imports":[
					{"type":"ortholog","name": "BRCA2_HUMAN","target":"primates.Human", "taxon" : "Homo_sapiens", "rank":"primates"}, 
					{"type":"ortholog","name": "BRCA2_HUMAN","target":"glires.Mouse", "taxon" : "Mus_musculus", "rank":"glires"}
					]},
{"name":"BRCA2_MOUSE", "taxon":"Mus_musculus", "rank":"glires","size":20544,"imports":[
					{"type":"ortholog","name": "BRCA2_HUMAN","target":"primates.Human", "taxon" : "Homo_sapiens", "rank":"primates"}, 
					{"type":"inparalog","name": "BRCA1_MOUSE","target":"glires.Mouse", "taxon" : "Mus_musculus", "rank":"glires"}, 
					{"type":"ortholog","name": "BRCA2_RAT","target":"glires.Rat", "taxon" : "Rattus_norvegicus", "rank":"glires"},
					{"type":"ortholog","name": "BRCA2_YEAST","target":"outgroup.Yeast", "taxon" : "Saccharomyces_cerevisiae", "rank":"outgroup"}
					]}
]