Selected element won't change in d3 - javascript

This snippet is a slightly modified version of the example http://bl.ocks.org/4063550 of a Reingold-Tilford Tree. Everything is similar, except I have slightly changed the code where one appends the text to a node. I want to attach an id to each node text so that I can later tweak the text a bit so the graph is more readable.
Here I add the id's and slightly tweak the rotation of the entire diagram. My changes are enclosed with the **.
node.append("text")
.attr("dy", ".31em")
**.attr("id", function(d,i) {return "n" + i;})**
.attr("text-anchor", function(d) { return d.x < 180 ? "start" : "end"; })
.attr("transform", function(d) { return d.x < 180 ? "**rotate(10)**translate(8)" :"**rotate(199)**translate(-8)"; })
.text(function(d) { return d.name; });
});
Right after the example code with the above being the only changes, I want to put the following line, for example, into the same script.
d3.select("#n1").text("test");
When I load the page it's not changing the text at node id = "n1"! If I put exactly that same line into the console with Firebug, it changes the n1 node's text as desired.
After browsing countless tutorials and the like, I think that I don't understand something fundamental about how this stuff works, any advice would be appreciated!
Edit: Here is the complete html file as requested! It is verbatim the example I linked above, with the only exceptions being what I described above.
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.node circle {
fill: #fff;
stroke: steelblue;
stroke-width: 1.5px;
}
.node {
font: 12px sans-serif;
}
.link {
fill: none;
stroke: #ccc;
stroke-width: 1.5px;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var diameter = 960;
var tree = d3.layout.tree()
.size([360, diameter / 2 - 120])
.separation(function(a, b) { return (a.parent == b.parent ? 1 : 2) / a.depth; });
var diagonal = d3.svg.diagonal.radial()
.projection(function(d) { return [d.y, d.x / 180 * Math.PI]; });
var svg = d3.select("body").append("svg")
.attr("width", diameter)
.attr("height", diameter)
.append("g")
.attr("transform", "translate(" + diameter / 2 + "," + diameter / 2 + ")");
d3.json("thesis.json", function(error, root) {
var nodes = tree.nodes(root),
links = tree.links(nodes);
var link = svg.selectAll(".link")
.data(links)
.enter().append("path")
.attr("class", "link")
.attr("d", diagonal);
var node = svg.selectAll(".node")
.data(nodes)
.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) { return "rotate(" + (d.x - 90) + ")translate(" + d.y + ")"; });
node.append("circle")
.attr("r", 4.5);
node.append("text")
.attr("dy", ".25em")
.attr("id", function(d,i) {return "n" + i;})
.attr("text-anchor", function(d) { return d.x < 180 ? "start" : "end"; })
.attr("transform", function(d) { return d.x < 180 ? "rotate(10)translate(8)" : "rotate(199)translate(-8)"; })
.text(function(d) { return d.name; });
});
d3.select(self.frameElement).style("height", diameter + "px");
d3.select("#n1").text("test");
</script>
</body>
</html>

The issue is that d3.json makes an asynchronous call. That is, the code within that block is only executed when the network call returns with the JSON data. The code after the block is executed immediately though. So, you are trying to modify an element that does not exist yet.
The solution is to move the code that modifies the graph inside the d3.json block, or into a function that is called inside that block.

Related

Add d3-tip infobox to d3.js SVG

Would like to add an infobox to a d3.js file.
Have studied this SO question, and this SO question, and this SO question, and this fiddle.
Most of the following HTML + JavaScrip + d3.js + JSON file works as planned.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="http://labratrevenge.com/d3-tip/javascripts/d3.tip.v0.6.3.js" charset="utf-8"></script>
<style type="text/css">
.node { cursor: pointer; }
.node circle { fill: #fff; stroke: steelblue; stroke-width: 1.5px; }
.node text { font: 10px sans-serif; }
.link { fill: none; stroke: #ccc; stroke-width: 1.5px; }
.link:hover { stroke:blue; }
div#tooltip{ color:#ffffff; background:#000000; opacity:1; padding:5px; }
</style>
<title>Soils with Local JSON</title>
</head>
<body>
<h1>Soils with Local JSON</h1>
<div id="tooltip" style="display:none"></div>
<script type="text/javascript">
var margin = {
top: 20,
right: 120,
bottom: 20,
left: 120
},
width = 1000 - margin.right - margin.left,
height = 400 - margin.top - margin.bottom;
var i = 0,
duration = 750,
root;
var tree = d3.layout.tree()
.size([height, width]);
var diagonal = d3.svg.diagonal()
.projection(function (d) {
return [d.y, d.x];
});
//Add tool tip: d3-tip.
var tip = d3.tip()
.attr('class', 'd3-tip')
.html(function(d) { return '' + d.name + '' });
var svg = d3.select("body").append("svg")
.attr("width", width + margin.right + margin.left)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
//Call tool-tip in var svg =
.call(tip);
var root = {
"name":"Soil","children":[
{"name":"Albaqualfs","url":"http://en.wikipedia.org/wiki/Albaqualfs","children":[
{"name":"Aeric Albaqualfs","children":[
{"name":"Auxvasse"},
{"name":"Cayagua"},
{"name":"Mamou"},
{"name":"Marine"},
{"name":"Medoc"},
{"name":"Springfield"},
{"name":"Tenot"}]}]}]};
root.x0 = height / 2;
root.y0 = 0;
function collapse(d) {
if (d.children) {
d._children = d.children;
d._children.forEach(collapse);
d.children = null;
}
}
root.children.forEach(collapse);
update(root);
d3.select(self.frameElement).style("height", "800px");
function update(source) {
// Compute the new tree layout.
var nodes = tree.nodes(root).reverse(),
links = tree.links(nodes);
// Normalize for fixed-depth.
nodes.forEach(function (d) {
d.y = d.depth * 180;
});
// Update the nodes
var node = svg.selectAll("g.node")
.data(nodes, function (d) {
return d.id || (d.id = ++i);
});
// Enter any new nodes at the parent's previous position.
var nodeEnter = node.enter().append("g")
.attr("class", "node")
.attr("transform", function (d) {
return "translate(" + source.y0 + "," + source.x0 + ")";
})
.on("click", click);
nodeEnter.append("circle")
.attr("r", 1e-6)
.style("fill", function (d) {
return d._children ? "lightsteelblue" : "#fff";
});
nodeEnter.append("text")
.attr("x", function (d) {
return d.children || d._children ? -10 : 10;
})
.attr("dy", ".35em")
.attr("text-anchor", function (d) {
return d.children || d._children ? "end" : "start";
})
.text(function (d) {
return d.name;
})
.style("fill-opacity", 1e-6)
.on("mouseover", function (d) {
var r = d3.select(this).node().getBoundingClientRect();
d3.select("div#tooltip")
.style("display", "inline")
.style("top", (r.top-25) + "px")
.style("left", r.left + "px")
.style("position", "absolute")
.text(d.test);
})
.on("mouseout", function(){
d3.select("div#tooltip").style("display", "none")
});
// Transition nodes to their new position.
var nodeUpdate = node.transition()
.duration(duration)
.attr("transform", function (d) {
return "translate(" + d.y + "," + d.x + ")";
});
nodeUpdate.select("circle")
.attr("r", 4.5)
.style("fill", function (d) {
return d._children ? "lightsteelblue" : "#fff";
});
nodeUpdate.select("text")
.style("fill-opacity", 1);
// Transition exiting nodes to the parent's new position.
var nodeExit = node.exit().transition()
.duration(duration)
.attr("transform", function (d) {
return "translate(" + source.y + "," + source.x + ")";
})
.remove();
nodeExit.select("circle")
.attr("r", 1e-6);
nodeExit.select("text")
.style("fill-opacity", 1e-6);
// Update the links
var link = svg.selectAll("path.link")
.data(links, function (d) {
return d.target.id;
});
// Enter any new links at the parent's previous position.
link.enter().insert("path", "g")
.attr("class", "link")
.attr("d", function (d) {
var o = {
x: source.x0,
y: source.y0
};
return diagonal({
source: o,
target: o
});
});
// Transition links to their new position.
link.transition()
.duration(duration)
.attr("d", diagonal);
// Transition exiting nodes to the parent's new position.
link.exit().transition()
.duration(duration)
.attr("d", function (d) {
var o = {
x: source.x,
y: source.y
};
return diagonal({
source: o,
target: o
});
})
.remove();
// Stash the old positions for transition.
nodes.forEach(function (d) {
d.x0 = d.x;
d.y0 = d.y;
});
}
// Toggle children on click.
function click(d) {
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
update(d);
}
</script>
</body>
</html>
Now would like to add an infobox for an item that displays information like this:
Am currently working with d3-tip.
The included file has a JSON field for "name" and "url". It also includes an attempt to map the "url" to d.url and "name" to d.name.
One issue is the need to keep the infobox temporarily persistent so that users can click the item's URL if they choose. Once the cursor is moved away from the infobox, it should clear.
The target infobox image above shows an X to close the infobox if clicked. However, I don't see how to introduce a second selector - the current selector opens the next level of the tree. Therefore, I'm hoping that a temporarily persistent infobox, revealed on mouseover, can be implemented.
The infobox can overlay the tree data - no need to re-draw the tree to adjust to the placement of the infobox.
How to correct the d3-tip code to work as desired?
If an SO reader uses an alternative to d3-tip that basically accomplishes the same task, that would be good too.

How to generate D3.js circular dendrogram code from Python

The following figure is generated with D3.js.
based on the code here:
<!DOCTYPE html>
<meta charset="utf-8">
<title>Flare Dendrogram</title>
<style>
.node circle {
fill: #fff;
stroke: steelblue;
stroke-width: 1.5px;
}
.node {
font: 10px sans-serif;
}
.link {
fill: none;
stroke: #ccc;
stroke-width: 1.5px;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var radius = 960 / 2;
var cluster = d3.layout.cluster()
.size([360, radius - 120]);
var diagonal = d3.svg.diagonal.radial()
.projection(function(d) { return [d.y, d.x / 180 * Math.PI]; });
var svg = d3.select("body").append("svg")
.attr("width", radius * 2)
.attr("height", radius * 2)
.append("g")
.attr("transform", "translate(" + radius + "," + radius + ")");
d3.json("/d/4063550/flare.json", function(error, root) {
var nodes = cluster.nodes(root);
var link = svg.selectAll("path.link")
.data(cluster.links(nodes))
.enter().append("path")
.attr("class", "link")
.attr("d", diagonal);
var node = svg.selectAll("g.node")
.data(nodes)
.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) { return "rotate(" + (d.x - 90) + ")translate(" + d.y + ")"; })
node.append("circle")
.attr("r", 4.5);
node.append("text")
.attr("dy", ".31em")
.attr("text-anchor", function(d) { return d.x < 180 ? "start" : "end"; })
.attr("transform", function(d) { return d.x < 180 ? "translate(8)" : "rotate(180)translate(-8)"; })
.text(function(d) { return d.name; });
});
d3.select(self.frameElement).style("height", radius * 2 + "px");
</script>
Is there a way I can use Python to generate the above exact figure (HTML)?
Assuming that you want to avoid having to write javascript and using python instead, you could have a look at pyjs. From their website:
pyjs contains a Python-to-JavaScript compiler, an AJAX framework and a
Widget Set API. pyjs started life as a Python port of Google Web Toolkit,
the Java-to-JavaScript compiler.
I haven't used it and am unsure how you would go about including the d3 library. However their wiki has the following page:
https://github.com/pyjs/pyjs/wiki/Calling-a-jQuery-component-from-Pyjs
That article might give you an idea of whether pyjs is useful to you.
Is there a way I can use Python to generate the above exact figure (HTML)?
Not too dissimilar to pandita's suggestion, you could use Brython, which allows you to run Python in a web browser. Some sample skeleton code (using D3.js; I don't know of any Python libraries that give you a dendogram result as good as D3.js.):
<!DOCTYPE html>
<meta charset="utf-8">
<title>Flare Dendrogram</title>
<style>
/* CSS goes here */
</style>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="brython.js"></script>
<script type="text/python">
#
# Python code to generate dendogram here
# (equivalent to the sample JavaScript code provided)
#
</script>
<body onload="brython()">
</body>
</html>
But this doesn't seem like a great solution.
You may also find this StackOverflow page helpful: How do I create a radial cluster like the following code-example in Python?

Getting Sankey diagram in D3 from csv file

I am attempting to create a Sankey diagram from a csv file. I am utilizing code provided by timelyportfolio, and also the code from the d3 site (and even the sample csv files). However, when I try to run the code in Chrome, I am getting a blank Html page, indicating that the code is crashing. I attempted to redirect the source codes to files on my desktop, but I am still running into the same issues. (I am working on a computer with Windows XP)
I have pasted the code below:
<!DOCTYPE html>
<meta charset="utf-8">
<title>SANKEY Experiment</title>
<style>
.node rect {
cursor: move;
fill-opacity: .9;
shape-rendering: crispEdges;
}
.node text {
pointer-events: none;
text-shadow: 0 1px 0 #fff;
}
.link {
fill: none;
stroke: #000;
stroke-opacity: .2;
}
.link:hover {
stroke-opacity: .5;
}
</style>
<body>
<p id="chart">
<script src="http://d3js.org/d3.v3.js"></script>
<script src="C:\Documents and Settings\jennifer.ducz\Desktop\sankey.js"></script>
<script>
var units = "Units";
var margin = {top: 10, right: 10, bottom: 10, left: 10},
width = 1400 - margin.left - margin.right,
height = 740 - margin.top - margin.bottom;
var formatNumber = d3.format(",.0f"), // zero decimal places
format = function(d) { return formatNumber(d) + " " + units; },
color = d3.scale.category20();
// append the svg canvas to the page
var svg = d3.select("#chart").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
// Set the sankey diagram properties
var sankey = d3.sankey()
.nodeWidth(36)
.nodePadding(10)
.size([width, height]);
var path = sankey.link();
// load the data with d3.csv instead of d3.json
//for another much simpler example uncomment the below
d3.csv("C:\Documents and Settings\jennifer.ducz\Desktop\sankey.csv", function(error, data) {
//d3.csv("d3noob_energy.csv", function(error, data) {
//set up graph in same style as original example but empty
graph = {"nodes" : [], "links" : []};
data.forEach(function (d) {
graph.nodes.push({ "name": d.source });
graph.nodes.push({ "name": d.target });
graph.links.push({ "source": d.source, "target": d.target, "value": +d.value });
});
//thanks Mike Bostock https://groups.google.com/d/msg/d3-js/pl297cFtIQk/Eso4q_eBu1IJ
//this handy little function returns only the distinct / unique nodes
graph.nodes = d3.keys(d3.nest()
.key(function (d) { return d.name; })
.map(graph.nodes));
//it appears d3 with force layout wants a numeric source and target
//so loop through each link replacing the text with its index from node
graph.links.forEach(function (d, i) {
graph.links[i].source = graph.nodes.indexOf(graph.links[i].source);
graph.links[i].target = graph.nodes.indexOf(graph.links[i].target);
});
//now loop through each nodes to make nodes an array of objects rather than an array of strings
graph.nodes.forEach(function (d, i) {
graph.nodes[i] = { "name": d };
});
sankey
.nodes(graph.nodes)
.links(graph.links)
.layout(32);
// add in the links
var link = svg.append("g").selectAll(".link")
.data(graph.links)
.enter().append("path")
.attr("class", "link")
.attr("d", path)
.style("stroke-width", function(d) { return Math.max(1, d.dy); })
.sort(function(a, b) { return b.dy - a.dy; });
// add the link titles
link.append("title")
.text(function(d) {
return d.source.name + " → " +
d.target.name + "\n" + format(d.value); });
// add in the nodes
var node = svg.append("g").selectAll(".node")
.data(graph.nodes)
.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")"; })
.call(d3.behavior.drag()
.origin(function(d) { return d; })
.on("dragstart", function() {
this.parentNode.appendChild(this); })
.on("drag", dragmove));
// add the rectangles for the nodes
node.append("rect")
.attr("height", function(d) { return d.dy; })
.attr("width", sankey.nodeWidth())
.style("fill", function(d) {
return d.color = color(d.name.replace(/ .*/, "")); })
.style("stroke", function(d) {
return d3.rgb(d.color).darker(2); })
.append("title")
.text(function(d) {
return d.name + "\n" + format(d.value); });
// add in the title for the nodes
node.append("text")
.attr("x", -6)
.attr("y", function(d) { return d.dy / 2; })
.attr("dy", ".35em")
.attr("text-anchor", "end")
.attr("transform", null)
.text(function(d) { return d.name; })
.filter(function(d) { return d.x < width / 2; })
.attr("x", 6 + sankey.nodeWidth())
.attr("text-anchor", "start");
// the function for moving the nodes
function dragmove(d) {
d3.select(this).attr("transform",
"translate(" + (
d.x = Math.max(0, Math.min(width - d.dx, d3.event.x))
) + "," + (
d.y = Math.max(0, Math.min(height - d.dy, d3.event.y))
) + ")");
sankey.relayout();
link.attr("d", path);
}
});
</script>
</body>
</html>
If someone can tell me what I'm doing wrong, please let me know.
Edit: This is the sample data I'm using courtesy of timelyportfolio
source target value
Barry Elvis 2
Frodo Elvis 2
Frodo Sarah 2
Barry Alice 2
Elvis Sarah 2
Elvis Alice 2
Sarah Alice 4
I've never played with it; however,
1) run your developer tools/console to see exactly which line is crashing the app
2) the following link discusses problems/solutions in formatting the data
http://www.d3noob.org/2013/02/sankey-diagrams-description-of-d3js-code.html
if you are not using MAMP or any other kind of virtual server, Chrome won't load the csv or any other local files other than the html. Try using a local webserver and everything should be fine

D3 Bar Graph example not working locally

I am very new to D3, and wanted to see how an example would work locally. I copied and pasted the bar graph code to a local file called index.html, and also copied over the data.tsv. For some reason, absolutely nothing is showing up when I open the file on a browser! I tried changing the script src to "d3/d3.v3.min.js" because that is the folder the d3 I downloaded is in. However, this does not work either. For every example I have tried I have yet to successfully view a D3 example. Help would be appreciated!
The index.html code is as follows:
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.bar {
fill: steelblue;
}
.x.axis path {
display: none;
}
</style>
<body>
<script src="d3/d3.v3.min.js"></script>
<script>
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var formatPercent = d3.format(".0%");
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(formatPercent);
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
d3.tsv("data.tsv", type, function(error, data) {
x.domain(data.map(function(d) { return d.letter; }));
y.domain([0, d3.max(data, function(d) { return d.frequency; })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Frequency");
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.letter); })
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.frequency); })
.attr("height", function(d) { return height - y(d.frequency); });
});
function type(d) {
d.frequency = +d.frequency;
return d;
}
</script>
and the data.tsv is in the following format:
letter frequency
A .08167
B .01492
C .02780
D .04253
E .12702
F .02288
G .02022
H .06094
I .06973
The d3.tsv method makes an AJAX request for data. On most browsers, this won't work locally due to the Same Origin Policy, which generally prohibits AJAX requests to file:/// urls.
To get an example that uses AJAX running locally, you'll need a local webserver. If you have Python, running
> python -m SimpleHTTPServer
from the command line in the directory with your files will do it.
and if you are using python 3
> python -m http.server 9000
If you prefer node.js, try http-server.
As an alternative, and I was myself suggested by Lars Kotthoff when trying to work with .tsv/.csv files, you can work directly for this purpose on:
http://plnkr.co/
This enables you to work with all the .json / .tsv / .csv files you like, and share them with people for collaboration. You can do this anonymously or not, what matters is that you don't lose the then-generated HTTP address of your plunker.
One thing to pay attention to: you cannot upload directly the files in the way you would do on an FTP server, but you should instead:
Click on "new file"
Type the name of your .csv / .tsv / .json
file as referred to in your code (including the extension)
Copy
and paste the code contained in this file as is.
Don't forget
to update the names of your .css / .js if required so the match with
that of your index.html
As already stated, you're most likely encountering a CORS issue with the XHR in the d3 library for an external resource to parse the JSON data.
However, here is another solution: use JSONP and Express/Node.js in conjunction with a jQuery function to initiate a client-side request for JSON, instead of using the original wrapper for d3 functions.
Had to remove the original d3.json wrapper and populate the adjacency diagrams with data from the request. Begin by cloning or downloading jsonp-d3-experiment and start the server using node server.js. Of course you need to have Node.js installed globally, beginning with Node Packaged Modules (npm). Copy your program into a subdirectory.
Put your JSON data in the jsonp-d3-experiment directory and modify server.js to route the request to your data:
// Return data from callback
server.get('/example', function(req, res)
{
// Read the JSON data and send to JSONP response
readJSON('example.json', function (e, json)
{
if (e) { throw e; }
res.jsonp(json);
});
});
Below is the code I modified for a co-occurrence matrix. I moved the entire script into $.getJSON and removed the d3.json function altogether.
<script>
$.getJSON("http://localhost:8080/example?callback=?", function(result){
miserables = result;
var margin = {
top: 80,
right: 0,
bottom: 10,
left: 80
},
width = 720,
height = 720;
var x = d3.scale.ordinal().rangeBands([0, width]),
z = d3.scale.linear().domain([0, 4]).clamp(true),
c = d3.scale.category10().domain(d3.range(10));
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.style("margin-left", -margin.left + "px")
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var matrix = [],
nodes = miserables.nodes,
n = nodes.length;
// Compute index per node.
nodes.forEach(function(node, i) {
node.index = i;
node.count = 0;
matrix[i] = d3.range(n).map(function(j) {
return {
x: j,
y: i,
z: 0
};
});
});
// Convert links to matrix; count character occurrences.
miserables.links.forEach(function(link) {
matrix[link.source][link.target].z += link.value;
matrix[link.target][link.source].z += link.value;
matrix[link.source][link.source].z += link.value;
matrix[link.target][link.target].z += link.value;
nodes[link.source].count += link.value;
nodes[link.target].count += link.value;
});
// Precompute the orders.
var orders = {
name: d3.range(n).sort(function(a, b) {
return d3.ascending(nodes[a].name, nodes[b].name);
}),
count: d3.range(n).sort(function(a, b) {
return nodes[b].count - nodes[a].count;
}),
group: d3.range(n).sort(function(a, b) {
return nodes[b].group - nodes[a].group;
})
};
// The default sort order.
x.domain(orders.name);
svg.append("rect")
.attr("class", "background")
.attr("width", width)
.attr("height", height);
var row = svg.selectAll(".row")
.data(matrix)
.enter().append("g")
.attr("class", "row")
.attr("transform", function(d, i) {
return "translate(0," + x(i) + ")";
})
.each(row);
row.append("line")
.attr("x2", width);
row.append("text")
.attr("x", -6)
.attr("y", x.rangeBand() / 2)
.attr("dy", ".32em")
.attr("text-anchor", "end")
.text(function(d, i) {
return nodes[i].name;
});
var column = svg.selectAll(".column")
.data(matrix)
.enter().append("g")
.attr("class", "column")
.attr("transform", function(d, i) {
return "translate(" + x(i) + ")rotate(-90)";
});
column.append("line")
.attr("x1", -width);
column.append("text")
.attr("x", 6)
.attr("y", x.rangeBand() / 2)
.attr("dy", ".32em")
.attr("text-anchor", "start")
.text(function(d, i) {
return nodes[i].name;
});
function row(row) {
var cell = d3.select(this).selectAll(".cell")
.data(row.filter(function(d) {
return d.z;
}))
.enter().append("rect")
.attr("class", "cell")
.attr("x", function(d) {
return x(d.x);
})
.attr("width", x.rangeBand())
.attr("height", x.rangeBand())
.style("fill-opacity", function(d) {
return z(d.z);
})
.style("fill", function(d) {
return nodes[d.x].group == nodes[d.y].group ? c(nodes[d.x].group) : null;
})
.on("mouseover", mouseover)
.on("mouseout", mouseout);
}
function mouseover(p) {
d3.selectAll(".row text").classed("active", function(d, i) {
return i == p.y;
});
d3.selectAll(".column text").classed("active", function(d, i) {
return i == p.x;
});
}
function mouseout() {
d3.selectAll("text").classed("active", false);
}
d3.select("#order").on("change", function() {
clearTimeout(timeout);
order(this.value);
});
function order(value) {
x.domain(orders[value]);
var t = svg.transition().duration(2500);
t.selectAll(".row")
.delay(function(d, i) {
return x(i) * 4;
})
.attr("transform", function(d, i) {
return "translate(0," + x(i) + ")";
})
.selectAll(".cell")
.delay(function(d) {
return x(d.x) * 4;
})
.attr("x", function(d) {
return x(d.x);
});
t.selectAll(".column")
.delay(function(d, i) {
return x(i) * 4;
})
.attr("transform", function(d, i) {
return "translate(" + x(i) + ")rotate(-90)";
});
}
var timeout = setTimeout(function() {
order("group");
d3.select("#order").property("selectedIndex", 2).node().focus();
}, 5000);
});
</script>
Notice that now the JSON data is in result, so the easiest thing to do was to assign it to miserables.
Note: jQuery is required for this solution.
Now you should be able to locally open and render all your d3 visualizations without hosting them on a server--simply open them in your browser straight from your local filesystem.
HTH!

Add labels to D3 Chord diagram

I'm a rookie programmer, so this one will probably be an easy one for most of you. What lines of code do I need for labels and/or mouse-over text for this Chord diagram?
http://mbostock.github.com/d3/ex/chord.html
I need it to display the name of the category in the outer strip. When you mouse over, I want to display the exact number and both categories. Something like this: 'A: 5 thing from B'.
EDIT:
I still can't figure out how to implement it in my code. Can someone fill in my example code an explain what's going on?
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>Selecties EK 2010</title>
<script type="text/javascript" src="d3.v2.js"></script>
<link type="text/css" rel="stylesheet" href="ek2010.css"/>
</head>
<body>
<div id="chart"></div>
<script type="text/javascript" src="ek2010.js"></script>
</body>
</html>
and
// From http://mkweb.bcgsc.ca/circos/guide/tables/
var chord = d3.layout.chord()
.padding(.05)
.sortSubgroups(d3.descending)
.matrix([
[0, 0, 7, 5],
[0, 0, 8, 3],
[7, 8, 0, 0],
[5, 3, 0, 0]
]);
var width = 1000,
height = 1000,
innerRadius = Math.min(width, height) * .3,
outerRadius = innerRadius * 1.1;
var fill = d3.scale.ordinal()
.domain(d3.range(4))
.range(["#000000", "#FFDD89", "#957244", "#F26223"]);
var svg = d3.select("#chart")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
svg.append("g")
.selectAll("path")
.data(chord.groups)
.enter().append("path")
.style("fill", function(d) { return fill(d.index); })
.style("stroke", function(d) { return fill(d.index); })
.attr("d", d3.svg.arc().innerRadius(innerRadius).outerRadius(outerRadius))
.on("mouseover", fade(.1))
.on("mouseout", fade(1));
var ticks = svg.append("g")
.selectAll("g")
.data(chord.groups)
.enter().append("g")
.selectAll("g")
.data(groupTicks)
.enter().append("g")
.attr("transform", function(d) {
return "rotate(" + (d.angle * 180 / Math.PI - 90) + ")"
+ "translate(" + outerRadius + ",0)";
});
ticks.append("line")
.attr("x1", 1)
.attr("y1", 0)
.attr("x2", 5)
.attr("y2", 0)
.style("stroke", "#000");
ticks.append("text")
.attr("x", 8)
.attr("dy", ".35em")
.attr("text-anchor", function(d) {
return d.angle > Math.PI ? "end" : null;
})
.attr("transform", function(d) {
return d.angle > Math.PI ? "rotate(180)translate(-16)" : null;
})
.text(function(d) { return d.label; });
svg.append("g")
.attr("class", "chord")
.selectAll("path")
.data(chord.chords)
.enter().append("path")
.style("fill", function(d) { return fill(d.target.index); })
.attr("d", d3.svg.chord().radius(innerRadius))
.style("opacity", 1);
/** Returns an array of tick angles and labels, given a group. */
function groupTicks(d) {
var k = (d.endAngle - d.startAngle) / d.value;
return d3.range(0, d.value, 1).map(function(v, i) {
return {
angle: v * k + d.startAngle,
label: i % 5 ? null : v / 1 + " internat."
};
});
}
/** Returns an event handler for fading a given chord group. */
function fade(opacity) {
return function(g, i) {
svg.selectAll("g.chord path")
.filter(function(d) {
return d.source.index != i && d.target.index != i;
})
.transition()
.style("opacity", opacity);
};
}
Add text elements to display labels. Alternatively, use textPath elements if you want to display text along a path. Two examples of labeled chord diagrams:
http://mbostock.github.com/d3/talk/20111018/chord.html
http://bl.ocks.org/1308257
You need to look at the (selection.on()) event handler in the d3.js wiki on Github. That shows you how to add events to elements including mouseover and mouseout. If you look at that example you linked to, you can see an instance of it already:
svg.append("g")
.selectAll("path")
.data(chord.groups)
.enter().append("path")
.style("fill", function(d) { return fill(d.index); })
.style("stroke", function(d) { return fill(d.index); })
.attr("d", d3.svg.arc().innerRadius(innerRadius).outerRadius(outerRadius))
.on("mouseover", fade(.1))
.on("mouseout", fade(1));
If you hover the mouse over the chord groups in the outer ring you will see all the other chord groups fade out.
If you want labels to pop-up that contain strings (text) you will need to define them using another JS library. One I know that works is Tipsyand there is an example using it together with d3 here. You should then be able to simply use a selector to choose which SVG element you want to illustrate this behavior.
Hope that helps.

Categories