How can I add axis labels to a sankey diagram in d3? - d3.js

I want to add axis on a sankey diagram. The code for the chart can be found in the following links: https://github.com/irbp005/d3SankeyAndLineInteraction
The visual representation of the char is as follows:
Ans I want to add labels like:
Basically in both sides of the y axis. Any ideas on how can I achieve this?

This should be fairly straightforward. Add a g element for each side and apply a translation transform to position it in the x axis and then use something along the lines of this:
selection.append("text")
.attr("class", "axis-label")
.attr("transform", "rotate(-90)")
.attr("y", -50)
.attr("x", -height/2)
.attr("fill", "#000")
.style("text-anchor", "middle")
.text("Y Label");
Look through the 1st example in Chapter 1 here that explains the addition of X and Y labels to the plot axes:
https://leanpub.com/d3-t-and-t-v4/read

Related

DC chart heatmap columns text rotation doesn't work [duplicate]

I am new to d3 and svg coding and am looking for a way to rotate text on the xAxis of a chart. My problem is that typically the xAxis titles are longer than the bars in the bar chart are wide. So I'm looking to rotate the text to run vertically (rather than horizontally) beneath the xAxis.
I've tried adding the transform attribute:
.attr("transform", "rotate(180)")
But when I do that, the text disappears altogether. I've tried increasing the height of the svg canvas, but still was unable to view the text.
Any thoughts on what I'm doing wrong would be great. Do I need to also adjust the x and y positions? And, if so, by how much (hard to troubleshoot when I can see it in Firebug).
If you set a transform of rotate(180), it rotates the element relative to the origin, not relative to the text anchor. So, if your text elements also have an x and y attribute set to position them, it’s quite likely that you’ve rotated the text off-screen. For example, if you tried,
<text x="200" y="100" transform="rotate(180)">Hello!</text>
the text anchor would be at ⟨-200,100⟩. If you want the text anchor to stay at ⟨200,100⟩, then you can use the transform to position the text before rotating it, thereby changing the origin.
<text transform="translate(200,100)rotate(180)">Hello!</text>
Another option is to use the optional cx and cy arguments to SVG’s rotate transform, so that you can specify the origin of rotation. This ends up being a bit redundant, but for completeness, it looks like this:
<text x="200" y="100" transform="rotate(180,200,100)">Hello World!</text>
Shamelessly plucked from elsewhere, all credit to author.
margin included only to show the bottom margin should be increased.
var margin = {top: 30, right: 40, bottom: 50, left: 50},
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", "rotate(-65)");
One problem with this rotating D3 axis labels is that you have to re-apply this logic each time you render the axis. This is because you do not have access to the enter-update-exit selections that the axis uses to render the ticks and labels.
d3fc is a component library that has a decorate pattern allowing you to gain access to the underling data join used by components.
It has a drop-in replacement for the D3 axis, where axis label rotation is performed as follows:
var axis = fc.axisBottom()
.scale(scaleBand)
.decorate(function(s) {
s.enter()
.select('text')
.style('text-anchor', 'start')
.attr('transform', 'rotate(45 -10 10)');
});
Notice that the rotation is only applied on the enter selection.
You can see some other possible uses for this pattern on the axis documentation page.

How to create Stacked Line Chart D3, Multiple Y Axis and common X Axis

I am triying to create a line chart which will have multiple y axis but a common x axis using d3, can somebod provide me the example how to create it with D3 library. It should be something like as shown below
Quite simply: Just draw 2 charts but only append one x-axis, here's a fiddle to get you started: http://jsfiddle.net/henbox/fg18eru3/1/
In this example, I've assumed that the two different datasets have different y-domains but the same x-domain. If that's not the case, you should just get the max and min from the combined x-domains of both sets.
If you start by defining 2 g elements that will contain the two charts, and transforming the bottom chart down so they don't overlap:
var topchart = svg.append("g").attr("class", "topchart");
var bottomchart = svg.append("g").attr("class", "bottomchart")
.attr("transform", "translate(0," + height/2 + ")");
... then append the path and y-axis to the appropriate g, but only add the x-axis to the bottom chart:
bottomchart.append("g")
.attr("class", "axis x-axis")
.attr("transform", "translate(0," + (height/2 - padding) + ")")
.call(xAxis);

Graph ~ axis alignment issue

I'm currently working on a quite basic graph using 2 ordinal axes. X axis shows 4 categories, Y axis shows 3. For some reason, the plotted circles don't align with the plotted axes.
An example can be seen at http://jsfiddle.net/SrdY6/. Problem seems to be translation-related, but the only translation in there is applied to the large containing <g> element:
var lunchgraph = svg.append("g")
.attr("class", "lunchgraph")
.attr("transform", "translate(" + lunchmargin.left + "," + lunchmargin.top + ")");
I've been looking at this for some time now, but can't spot where things go wrong... Anyone with more insight?
Nothing like putting a question out there and risking public shame, only to find out the answer within minutes after posting.
For ordinal axes configured with rangeBands or rangeRoundBands, the scale function returns the lower value of the given input. To have the plot align with the exact categorical labels, you need to add half of the rangeBand to the calculated coordinate.
So: no problem with the translations or anything, but with the computation of cx and cy coordinates for placing the circles in the graph.
Correct code:
.attr("cx", function(d) { return x(d.label) + x.rangeBand()/2 ;} )
.attr("cy", function(d) { return y(d.sqid) + y.rangeBand()/2 ; } )

draw a grid or rectangles using a scale

I'm building my first line graph in d3:
http://jsfiddle.net/j94RZ/
I want to know how to utilize either the scale or axis allow me to draw a grid (of, presumably rectangles) where I can set a different background colour for each of the section of the grid...so I can alternate colours for each cell of the grid. I want the grid to be drawn and be constrained by the axes of my graph and then also adapt if the spacing of the axes ticks change (i.e. the axes changes like this: http://bl.ocks.org/mbostock/1667367). So if my graph has an x axis with 4 ticks and a y axis of 7 ticks then my graph will have a background grid that's 7 blocks high and 4 blocks wide.
I've been playing with the idea of using a range which starts at zero and ends at the full width of the graph but I don't know what value I can use for the step. Is there any way to sort of query the axis and return how many ticks there are?
var gridRange = d3.range(0, width, step?);
A better approach than your current solution would be to use scale.ticks() explicitly to get the tick values. The advantage of that is that it will still work if you change the number of ticks for some reason.
To get an alternating grid pattern instead of a single fill, you can use something like this code.
.attr("fill", function(d, i) {
return (i % 2) == 1 ? "green" : "blue";
})
Finally, to get the full grid pattern, you can either use an explicit loop as you've suggested, or nested selections. The idea here is to first pass in the y ticks, create a g element for each and then pass the x ticks to each one of these groups. In code, this looks something like this.
svg.selectAll("g.grid")
.data(y.ticks()).enter().append("g").attr("class", "grid")
.selectAll("rect")
.data(x.ticks()).enter().append("rect");
To set the position, you can access the indices within the top and bottom level data arrays like this.
.attr("x", function(d, i) {
return xScale(i);
})
.attr("y", function(d, i, j) {
return yScale(j);
})
To set the x position, you need the index of the inner array (passed to the set of g elements), which can be accessed through the second argument of your callback. For the outer array, simply add another argument (j here).
And that's really all there is to it. Complete jsfiddle here. To update this grid dynamically, you would simply pass in the new tick values (gotten from scale.ticks()), match with the existing data, and handle the enter/update/exit selections in the usual manner.
If you want to do without the auxiliary scales (i.e. without .rangeBand()), you can calculate the width/height of the rectangles by taking the extent of the range of a scale and dividing it by the number of ticks minus 1. Altogether, this makes the code a bit uglier (mostly because you need one fewer rectangle than ticks and therefore need to subtract/remove), but a bit more general. A jsfiddle that takes this approach is here.
So after a few helpful comments above I've got close to a solution. Using Ordinal rangebands get me close to where I want to go.
I've created the range bands by using the number of ticks on my axis as a basis for the range of the input domain:
var xScale = d3.scale.ordinal()
.domain(d3.range(10))
.rangeRoundBands([0, width],0);
var yScale = d3.scale.ordinal()
.domain(d3.range(4))
.rangeRoundBands([0, height],0);
I've then tried drawing the rectangles out like so:
svg.selectAll("rect")
.data(p)
.enter()
.append("rect")
.attr("x", function(d, i) {
return xScale(i);
})
.attr("y", function(d,i) {
0
})
.attr("width", xScale.rangeBand())
.attr("height", yScale.rangeBand())
.attr("fill", "green").
attr('stroke','red');
This gets me the desired effect but for only one row deep:
http://jsfiddle.net/Ny2FJ/2/
I want,somehow to draw the green blocks for the whole table (and also without having to hard code the amount of ticks in the ordinal scales domain). I tried to then apply the range bands to the y axis like so (knowing that this wouldn't really work though) http://jsfiddle.net/Ny2FJ/3/
svg.selectAll("rect")
.data(p)
.enter()
.append("rect")
.attr("x", function(d, i) {
return xScale(i);
})
.attr("y", function(d,i) {
return yScale(i);
})
.attr("width", xScale.rangeBand())
.attr("height", yScale.rangeBand())
.attr("fill", "green").
attr('stroke','red');
The only way I can think to do this is to introduce a for loop to run the block of code in this fiddle http://jsfiddle.net/Ny2FJ/2/ for each tick of the y axis.

d3 axis orientation: center

in D3, how can I put an axis in the middle, or center, of a graph? The documentation says "only top/bottom/left/right".
I know the dimensions of my graph, let's say it is 400px by 400px. In Protovis I used
vis.add(pv.Rule).bottom(200)
placing an axis 200px up from the bottom. How can I do this in D3?
You can transform the axes whichever way you want. The orientation only refers to which side the ticks and numbers are placed on.
See e.g. http://alignedleft.com/tutorials/d3/axes/
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + (h - padding) + ")")
.call(xAxis);
where you just need to change the parameter to the transform to get whatever you want.

Resources