I'm using http://bl.ocks.org/mbostock/1283663
and I'm trying to change the following code
d3.json("readme.json", function(root) {
hierarchy.nodes(root);
x.domain([0, root.value]).nice();
down(root, 0);
});
to a JSON.parse (some data). I don't have a problem pulling the JSON data but I am totally confused about what is being setup in the rest of the d3.json process with the hierarchy.node(root), x.domain and down(root)
You're probably going to have to read a little more documentation before having a working understanding of what's going in this function. This is a tricky example to start with; I'm just going to walk you through what my process of trying to understand would look like. Going line by line:
d3.json("readme.json", function(root) {
This loads the referenced json file and calls function with it. 'root' starts out equal to the json file.
hierarchy.nodes(root);
Looking through the code, we find where hierarchy is declared:
var hierarchy = d3.layout.partition()
.value(function(d) { return d.size; });
So hierarchy is some kind of layout and hierarchy.nodes will add some useful attributes to root that will make it easier to graph.
x.domain([0, root.value]).nice();
searching for "x" in the example we find x = d3.scale.linear().range([0, w]). Tt appears that x is a linear scale. Basically, the x function will transform values in the domain - [0, root.value] to the range [0, w]. w is the width of the svg. root.value is a little trickier. The node page says
value - the node value, as returned by the value accessor
But what is the value accessor? The initial declaration of indicates it has something todo with root's 'size' attribute, but what? At this point, the documentation starts to get pretty confusing so you might want to pop open the debugger and see exactly what the value attribute of root and root's children looks like.
down(root, 0);
The down function is unique to the example and well commented. Try reading through it while referencing the documentation and see if you can figure it out.
Related
I have a composite graph of two line charts. For one of them i'm attempting to apply a custom color range based on the value of each point on the line:
.colors(['rgb(215,48,39)','rgb(244,109,67)','rgb(253,174,97)','rgb(254,224,144)'])
.colorDomain ([0,3])
.colorAccessor (d,i) ->
if d.points[i].data.value.avg > 50
return 0
else
return 3
The problem is I keep getting only one color for the entire graph... Not to mention d returns as an object of all the points instead of a single point... (maybe a hint of the problem?)
Am i doing something wrong here and/or is there an easier way to do this?
You didn't get an answer so I'll try to look into it with you.
First, I created a fiddle at http://jsfiddle.net/djmartin_umich/4ZwaG/.
.colors( ['rgb(215,48,39)', 'rgb(244,109,67)', 'rgb(253,174,97)', 'rgb(254,224,144)' ] )
.colorDomain ([0,3])
.colorAccessor(function(d, i){
if(d[i] && d[i].data.value > 150)
return 3;
else if(d.data.value > 150)
return 2;
else return 1;
});
I had to play around with the color accessor to get it to stop throwing errors. The method was called twice with an array of elements and twice for each element in the array (24 times total).
Once I got it compiling I inspected the chart and saw this:
The chart has a path element that defines the line and a bunch of circles that define the points on the line. The points are part of the tool-tip that display when you hover over the different points on the line.
The path seems to be colored by the value returned when the array of values was passed in and the hover-points on the line are each colored by the value returned for that element.
So the path of the line is given a single color. It sounds like your expectation is for different portions of the line to be colored differently based on their y-value, but this is not how the line is rendered.
The article at http://www.d3noob.org/2013/01/applying-colour-gradient-to-graph-line.html describes how you can use gradients to achieve the effect you desire. I believe the author is "hard-coding" the start and stop points for each gradient, so it won't get you all the way to your answer but it should help you get started.
I hope this helps!
-DJ
I'm new to d3.js and trying to understand how to retrieve elements that are already on an html page.
If I try something like the following, 'd' is undefined when I look in the console log. I can access some of the td information via the 'this' keyword, but looking at the d3 API https://github.com/mbostock/d3/wiki/Selections#wiki-style, it says if style takes a value as a function, it passes the current 'datum' and index.
I can get the index fine, but datum is always undefined. Is there something obvious I'm missing to retrieve the values, or have the wrong way around ?
<table class="results">
<tr><td>13/1/0014</td><td>81</td><td>3</td><td></td></tr>
<tr><td>12/1/0014</td><td>690</td><td>47</td><td></td></tr>
<tr><td>5/1/0014</td><td>450</td><td>26</td><td></td></tr>
</table>
d3.selectAll(".results td:nth-child(4n+2)")
.style("background-color", function(d) {
console.log( d );
//change style depending on d, but d is always undefined
//I can access the elements via this, but not d ?
});
jsfiddle http://jsfiddle.net/a5WkH/2/
D3 assumes that you bind data to the elements using .data() or .datum(). If you haven't done that, something like function(d) { ... } won't work. Technically what happens is that D3 adds the data bound to a DOM element as the .__data__ attribute to that element. So if you really don't want to use .data() or .datum(), you could do something like this.
d3.selectAll(".results").each(function() { this.__data__ = d3.select(this).text(); });
This is what I have done here. It is usually better to use .data() though.
I'd like to implement a map that zooms in on an area similar to Mike's click-zoom-example http://bl.ocks.org/mbostock/2206590
In fact I have this working fine already. My problem is that I can't rely on the click event to implement the zoom — the zoom will be triggered by another event (a link). So when I get to this part of Mike's code:
function clicked(d) {
var x, y, k;
if (d && centered !== d) {
var centroid = path.centroid(d);
...
I'm a bit of a loss as I don't have 'd'. So, I'm assuming that I can instead, manually pass 'd' to my click function when I call it. But how do I actually select the feature (which is what 'd' represents) I want from the map?
To be a bit more concrete, I have a map of the world. The paths within the SVG group contain class information (e.g. the one for France looks like):
<path class="subunit FXX FRA" id="FXX" data-subunit="FXX" data-countryName="France" data-countryCode="FRA" d="M153.88838704622088,519........"></path>
How would I pass the 'France object' to the clicked(d) function? Or is there another approach altogether that I should be trying.
Any tips or help greatly appreciated.
You can use D3's select for this purpose:
d3.select(".FRA").each(function(d) {
// same code as inside clicked
});
Get the data associated with the France object:
d3.select('.FXX.FRA').datum()
And pass it to clicked:
clicked(d3.select('.FXX.FRA').datum())
I am attempting to get my head around using bound data with d3.js. I'm following the documentation and am now a little confused.
I want to produce donut charts with radii that vary depending on the data. I am comfortable producing the arcs to make up a donut using an array, but am having a hard time working out how to pass along a size parameter with the data binding of the arc. For example, if the data bound to the parent of the arc is something like {size: 20, cont: [1, 7]}, how can I bind the first element of the array as well as the size element? I have a fiddle attempting to show what I am talking about. In that example, the two donuts should be different sizes. I have commented out the kind of thing I suspect should be going on on line 14.
I have tried variations on:
var arcs = donuts.selectAll(".arc")
.data(function(d) { var temp = [];
temp.push(d.cont);
temp.push(d.size);
return temp; })
.enter()
.append("g")
.attr("class", "arc");
But it is clearly not producing what I expect.
The problem here isn't really the data inheritance, but the fact that you're passing the original data to a layout and then only the result of that to your drawing functions. The pie layout does store the original datum in the .data member of the result, but you're only passing it part of the original data.
The "proper" thing to do would be to refactor your data structure such that you can pass it in as-is and use the pie layout's .value() function to tell it how to access the data. Then you can directly access the original data.
There's however a quicker solution -- you can simply use the indices that are passed to your function to index into the original array. The code for this would look like this.
.attr("d", function(d, i, j) { return arc.outerRadius(dataset[j].size)(d); })
Note that you need two indices here because you have nested data -- i would be the index within your array of values for a single pie chart, whereas j denotes the index of the element at the level above that. Updated jsfiddle here.
I am trying to create an animated line graph where the line draws out its path from left to right, as in the first scene of this example. I have been closely following the code for mbostock's example, but still am having a couple issues.
When I try to run my code, I get an error saying "'d' is undefined", in the attr method inside the draw function shown below:
var line = d3.svg.line()
.interpolate('linear')
.x(function(d,i) {return x(i);})
.y(function(d,i) {return y(d['measures'][i]);});
chart.append('path')
.attr('class','line')
.data(array);
function draw(k) {
chart.select('.line')
.attr('d',function(d) { return line(d.measures.slice(0,k+1));});
}
var k=1, n = array.measures.length;
d3.timer( function() {
draw(k);
if((k+=2) >= n-1) {
draw(n-1);
//next transitions
return true;
}
This leads me to believe that my data has not been correctly bound to the path. However, I can't seem to understand why it would not be binding correctly
Any help would be much appreciated!
The example you picked as a starting point is binding the data for each ticker symbol to a <g> element, and then within each of those it's creating lines, etc. based on child data of that ticker. The .selectAll and .data methods are used when you're binding a list of data to a list of DOM nodes (e.g. <circle>, <rect>, <g>). But <path> is just a single DOM node with a long string of path data for rendering the line. You call the line function once to give you that string.
So for your purpose, if you just have one line that you're trying to animate, you just call your line function directly in order to create the path string to set on that node. There is no existing data bound to the node that you're trying to reuse. In your draw function:
.attr("d", line(array.measures.slice(0,k+1)))
Your line function itself needs slight adjustment too, assuming that array.measures contains a simple array of numbers:
var line = d3.svg.line()
.interpolate('linear')
.x(function(d,i) {return x(i);})
.y(function(d,i) {return y(d);});
BUT, having said all that, I think you would be better served by starting from a simpler example such as http://bl.ocks.org/duopixel/4063326 or one of the other approaches listed in this question or this question. It's hard to separate just the animated line portion of your original example from the rest of the cool stuff it's doing.