I'm working with D3's bullet chart and am trying to figure out how to display the actual measures number just to the right of the measures rectangle. Since I want to do this for every bullet chart, I figure it'd be best to do it right in the bullet.js code. I'm rather new to D3 so any help would be much appreciated! Here is the link to Mike Bostock's bullet chart example with the bullet.js included at the bottom.
It looks like the measures code is handled in this snippet:
// Update the measure rects.
var measure = g.selectAll("rect.measure")
.data(measurez);
measure.enter().append("rect")
.attr("class", function (d, i) { return "measure s" + i; })
.attr("width", w0)
.attr("height", height / 3)
.attr("x", reverse ? x0 : 0)
.attr("y", height / 3)
.transition()
.duration(duration)
.attr("width", w1)
.attr("x", reverse ? x1 : 0);
measure.transition()
.duration(duration)
.attr("width", w1)
.attr("height", height / 3)
.attr("x", reverse ? x1 : 0)
.attr("y", height / 3);
I thought I could just add something like this after the rect is appended but I've had no such luck.
measure.enter().append("text")
.attr("dy", "1em")
.text(function (d) { return d.measurez; })
.attr("x", reverse ? x0 : 0)
.attr("y", height / 3)
.transition()
.duration(duration)
.attr("width", w1)
.attr("x", reverse ? x1 : 0);
Thank you in advance for your consideration!
You almost got it -- there're just two small things to consider. First, you can't call .enter() twice. Once the enter selection has been operated on, it's merged into the update selection and your second selection will be empty. This is fixed easily by saving the selection in a variable, but in this case I would recommend making a separate selection for the text labels.
var measureLabel = g.selectAll("text.measure")
.data(measurez);
measureLabel.enter()....;
Second, to position the text to the right of the rect, you need to take not only the position, but also the width into account when computing the position of the text element. Also, you can omit a few elements that are not relevant to text elements.
measureLabel.enter()
.append("text")
.attr("class", function(d, i) { return "measure s" + i; })
.attr("dy", "1em")
.attr("dx", "1em")
.text(String)
.attr("x", reverse ? function(d) { return w0(d) + x0(d); } : w0)
.attr("y", height / 3);
measureLabel.transition()
.duration(duration)
.attr("x", reverse ? function(d) { return w1(d) + x1(d); } : w1);
Complete example here.
Related
I'm trying to have my text labels move and change while sorting. I have a multidimensional array including bar height, width, and opacity. A kind contributor helped me out yesterday with getting the bars sorted correctly, but I'd also like to get the text labels to move and change at the top of each bar as the bar sorts and moves.
var s = -1;
svg.on("click", function() {
s = (s + 1) % 3;
var xPosition = parseFloat(d3.select(this).attr("x")) xScale.bandwidth() / 2;
var yPosition = parseFloat(d3.select(this).attr("y")) + 14;
svg.selectAll("rect")
.sort(function(a, b) {
return d3.ascending(a[s], b[s]);
})
.transition()
.delay(function(d, i) {
return i * 65;
})
.duration(250)
.attr("x", function(d, i) {
return xScale(i);
});
svg.selectAll("text")
.append("text")
.text(function(d) {
return d[s];
})
.attr("text-anchor", "left")
.attr("x", xPosition)
.attr("y", yPosition)
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "red");
});
As you can see, I'm quite lost as to where to go moving forward. I took the xPosition and yPosition variable idea from a textbook but it doesn't seem to change anything. Also, the text labels never change while I sort through height, width, and opacity. Thanks for the help.
When you do this...
svg.selectAll("text")
.append("text")
//etc...
... you are appending <text> elements inside <text> elements, which makes no sense.
Instead of that, assuming that your texts were previously created, just select them and sort them accordingly:
svg.selectAll("text")
.text(function(d) {
return d[s];
})
.attr("x", xPosition)
.attr("y", yPosition);
Is it possible to add a piece of text in the centre of multiple rectangles in a D3 treemap chart? So what i'm after is something like this:
As you can see by the picture, I want to have text appear once in the middle of each “section” (different colours) on the chart. Is it possible to do this with D3? If so how would I achieve it.
Currently I've managed to have it appear on each individual rectangle, like so:
cell.append("svg:text")
.attr("x", function(d) { return d.dx / 2; })
.attr("y", function(d) { return d.dy / 2; })
.attr("dy", ".35em")
.text("test")
.style("opacity", function(d) { console.log(this.getComputedTextLength());d.w = this.getComputedTextLength(); return d.dx > d.w ? 1 : 0; });
Full code is here: http://jsfiddle.net/noobiecode/9ev9qjt3/74/
Any help will be appreciated.
For appending these texts, we first need to find the corresponding data. Right now, nodes have data regarding all the different depths (parents and children), but for these texts we only need the data for the second depth (depth == 2).
So, we first get the data:
var parents = treemap.nodes(root)
.filter(function(d) { return d.depth == 2 });
It gives us an array with three objects, so we are in the right way. This array (parents) have all we need to position the texts (x, y, dx, dy, name).
Then, we bind this data to the texts:
var parentsText = svg.selectAll(".parentsText")
.data(parents)
.enter()
.append("text");
After that, we append the texts:
parentsText.attr("text-anchor", "middle")
.attr("dominant-baseline", "central")
.attr("x", function(d){ return d.x + d.dx/2})
.attr("y", function(d){ return d.y + d.dy/2})
.text(function(d){ return d.name})
.attr("class", "parentText");
And this is the resulting fiddle: http://jsfiddle.net/gerardofurtado/9ev9qjt3/94/
You can also have the texts for each individual rectangle and for the group (but, in that case, you may want to move away some overlapping texts): http://jsfiddle.net/gerardofurtado/9ev9qjt3/95/
I want to modify charts I have used in D3 to be like those on the US government analytics. I am unsure where to start. The JSfiddle below is where I am starting from.
Do I have to modify the chart to a stacked bar chart, a bullet chart or simply create the recs and add more space between them and position labels above the charts?
Has anyone done anything similar with D3?
My starting point: http://jsfiddle.net/Monduiz/drhdtsaq/
The Y axis, rects and labels:
[...]
var y = d3.scale.ordinal()
.domain(["Earnings", "Industry", "Housing", "Jobs"])
.rangeRoundBands([0, height], 0.2);
[...]
bar.append("rect")
.attr("width", 0)
.attr("height", barHeight - 1)
.attr("fill", "#9D489A")
.attr("width", function(d){return x(d.value);});
bar.append("text")
.attr("class", "text")
.attr("x", function(d) { return x(d.value) - 3; })
.attr("y", barHeight / 2)
.attr("dy", ".35em")
.text(function(d) { return d.value; })
.attr("fill", "white")
.attr("font-family", "sans-serif")
.attr("font-size", "14px")
.attr("text-anchor", "end");
This is what I want to do:
EDIT
I have made some progress. Updated fiddle: http://jsfiddle.net/Monduiz/drhdtsaq/33/
Now I need to find how to move the ordinal categories between the bars.
Well, I got close enough after some attempts. I can polish things up with CSS.
updated fiddle: http://jsfiddle.net/Monduiz/drhdtsaq/66/
Basically, the solution is to create another set of rectangles called first using a second set of data to full value of the scale used. Then its just a matter of adjusting the size of the bars and positioning the labels.
bar2.append("rect")
.attr("height", y.rangeBand()-15)
.attr("fill", "#EDEDED")
.attr("width", function(d){return x(d);});
bar.append("rect")
.attr("height", y.rangeBand()-15)
.attr("fill", "#FFB340")
.attr("width", function(d){return x(d.value);});
In one bar chart I am working on I have created parallel ticks to x Axis in order to give references to the viewer.
The problem is in text I have added. The text is ordered inversely as it should and I have no idea how to keep correct order.
I understand the problem is here:
chart.selectAll("line")
.data(y.ticks(4))
.enter().append("line")
.attr("x1", 60)
.attr("x2", (barWidth + 50) * data.length)
.attr("y1", y)
.attr("y2", y)
.style("stroke", "#ccc");
chart.selectAll(".rule")
.data(y.ticks(4))
.enter().append("text")
.attr("class", "rule")
.attr("x", 30)
.attr("y", y)
.attr("text-anchor", "middle")
.text(String);
But I don't know how to fix it.
Here is the code in jsFiddle.
Thanks in advance.
The problem is that the y coordinates are counted from the top and not the bottom, so you need to reverse all the values. The updated jsfiddle is here; the relevant changes are below.
var y = d3.scale.linear()
.domain([0, d3.max(data, function(datum) { return datum.value; })])
.rangeRound([height-20, 0]);
Reversed the values in rangeRound such that the lowest value is mapped to the top.
chart.selectAll("rect")
.data(data)
.enter()
.append("svg:rect")
.attr("x", function(datum, index) { return x(index); })
.attr("y", function(datum) { return y(datum.value); })
.attr("height", function(datum) { return height - y(datum.value); })
...
Changed how y and height are computed accordingly.
I have a data variable which contains the following:
[Object { score="2.8", word="Blue"}, Object { score="2.8", word="Red"}, Object { score="3.9", word="Green"}]
I'm interested in modifying a piece of a D3 graph http://bl.ocks.org/3887051 to display the legend, which would be the list of the "word", for my data set.
The legend script looks like this (from link above):
var ageNames = d3.keys(data[0]).filter(function(key) { return key !== "State"; });
var legend = svg.selectAll(".legend")
.data(ageNames.slice().reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
How do I modify their ageNames function to display the "word" set from my data? I'm not sure how they're utilizing the d3.keys. Is there another way to do it?
This should work more or less, but you may need to reverse() (as the original example does) or otherwise rearrange the elements of words, in order to correctly map a word to the right color. Depends on how you've implemented your graph.
var words = yourDataArray.map(function(entry) { return entry.word; });
var legend = svg.selectAll(".legend")
.data(words)
// The rest stays the same