D3 - Position second x axis labels - d3.js

I have a stackblitz here - https://stackblitz.com/edit/d3-stacked-trend-bar-positioned-months-1b93nz?file=src%2Fapp%2Fbar-chart.ts
I have a stacked bar chart using D3 in Angular.
I'm trying to position a second axis of labels on the x axis.
Currently I'm using the with of the graph but this means the labels dont line up correctly with the bars.
Is there a better D3 way to add these labels and positino them.

Use the x-position of the bars and position the text in the middle of it.
private drawBarTitle(data:any){
this.chart.append('g')
.attr("transform", "translate(" + 10 + "," + (this.height+10) + ")")
.selectAll('g')
.data(data)
.enter()
.append('g')
.attr('transform', (d:any, i:any)=>{
var x = this.x(d.date) + (i%2) * 0.525 * this.x.bandwidth();
return "translate(" + (x + this.x.bandwidth()*0.95*0.25) + "," + 0 + ")"
})
.append('text')
.text((d:any, i:any)=>{ return d.type; })
.attr("dy", "-0.3em")
.classed('trend-type', true)
.style("text-anchor", "end")
.attr("transform", "rotate(-90)");
}

Related

D3JS: unable to set only 5 ticks in x Axis

I've been unable to set only 5 ticks on my x Axis, by default it set 10 ticks, multiples of 5, I tried to use ´.ticks(5)´ but it's not working for me. If if visualize the chart on mobile, 10 ticks becomes almost unreadable. But as I mentioned ´.ticks(5)´ it's not working
this is the snippet which draws the x axis:
var xAxis = svg.append("g")
.style("font-size", textSetter())
.attr("class", "xAxis", "axis")
.attr("transform", "translate(" + 0 + "," + heightTwo + ")")
.call(d3.axisBottom(xScale).ticks(5)
.tickPadding(5).tickFormat(d => d + "%"))
How can I solve this? Thanks
You could set all the text in the axis to a particular class and then hide the ticks based on the number for example.
//Create the xAxis and assign x-axisticks class to the tick text
var xAxis = svg.append("g")
.style("font-size", textSetter())
.attr("class", "xAxis", "axis")
.attr("transform", "translate(" + 0 + "," + heightTwo + ")")
.call(d3.axisBottom(xScale)
.selectAll('text')
.attr('class', 'x-axisticks');
//remove the ticks which based on the multiples
var xticks = d3.selectAll('.x-axisticks');
xticks.attr('class', function (d, i) {
i = i + 1;
if (i % 5 !== 0) d3.select(this).remove();
});
Hope this helps. A more specific use-case answer would probably require some more code to be posted from your end so that I can tailor an answer, but this should work.

d3 text only appears when added to a group. Why?

I have appended some text to a map I am working on in d3, here is my code:
countriesGroup = svg
.append("g")
.attr("id", "map");
//add background
countriesGroup.append('rect')
.attr("x", 0)
.attr("y", 0)
.attr("width", w)
.attr("height", h);
//Draw countries
countries = countriesGroup.selectAll("path")
.data(j.features)
.enter()
...Draw country borders
//Create Timer Text
countriesGroup.append("text") <-----This worked
.attr("transform", "translate(" + w/2 + "," + h/2 + ")")
.style("fill", "#ff0000")
.text("This is a test");
//Create Timer Text
svg.append("text") <-----This didn't
.attr("transform", "translate(" + w/2 + "," + h/2 + ")")
.style("fill", "#ff0000")
.text("This is a test");
I initially tried appending the Timer text directly to svg but that did not work. When I later tried appending it directly to the countriesGroup group, this worked. How come the text was only able to render when appended to the group, but not when appended directly to the svg?

How to draw vertical text as labels in D3

I'm trying to draw vertical labels for the heatmap that I'm working. I'm using the example from http://bl.ocks.org/tjdecke/5558084. Here is the part of the code that I've changed:
var timeLabels = svg.selectAll(".timeLabel")
.data(ife_nr)
.enter().append("text")
.text(function(d) {
return d;
})
.attr("x", function(d, i) {
return (i * gridSize);
})
.attr("y", 0)
//.style("text-anchor", "middle")
//.attr("transform", "translate(" + gridSize / 2 + '-5' + ")")
.attr("transform", "translate(" + gridSize/2 + '-8' + "), rotate(-90)")
.attr("class", function(d, i) {
return ((i >= 0) ? "timeLabel mono axis axis-worktime" : "timeLabel mono axis");
});
But it appears the labels seems to be stacked on top one another on top of the first grid. How can I edit this code to get the labels correctly displayed?
Two problems: first, the translate should have a comma separating the values:
"translate(" + gridSize/2 + ",-8), rotate(-90)")
Assuming that -8 is the y value for the translate. If you don't have a comma, the value inside the parenthesis should be just the x translation (If y is not provided, it is assumed to be zero). But even if there is actually no comma and all that gridSize/2 + '-8' is just the x value you still have a problem, because number plus string is a string. You'll have to clarify this point.
Besides that, for rotating the texts over their centres, you'll have to set the cx and cy of the rotate. Have a look at this demo:
var svg = d3.select("body")
.append("svg")
.attr("width", 400)
.attr("height", 100);
var texts = svg.selectAll(".texts")
.data(["foo", "bar", "baz"])
.enter()
.append("text");
texts.attr("y", 50)
.attr("x", function(d,i){ return 50 + 80*i})
.text(function(d){ return d});
texts.attr("transform", function(d,i){
return "rotate(-90 " + (50 + 80*i) + " 50)";
});
<script src="https://d3js.org/d3.v4.js"></script>

Rotate D3 symbol

I'm rendering a d3 symbol that looks like this:
svg.append('path')
.attr("d", d3.svg.symbol().type("triangle-up").size(10))
.attr("transform", function(d) { return "translate(" + 100 + "," + 100 + ")"; })
.style("fill", "red")
I'd like to rotate this triangle so that the triangle points left <|. How can I rotate this symbol while keeping it at the same position in my viz? I've been trying to do the following, but the symbol moves to the upper left corner of my viz (it doesn't stay in the position created by the transformation):
svg.append('path')
.attr("d", d3.svg.symbol().type("triangle-up").size(10))
.attr("transform", function(d) { return "translate(" + 100 + "," + 100 + ")"; })
.attr("transform", "rotate(-45)")
.style("fill", "red")
The problem is that the second call to set transform overrides the first value here:
.attr("transform", function(d) { return "translate(" + 100 + "," + 100 + ")"; })
.attr("transform", "rotate(-45)") // Only this value will remain as the value of the attr
To fix it, you should append the rotation to the original value of transform:
.attr("transform", function(d) { return "translate(" + 100 + "," + 100 + ") rotate(-45)"; })
Another solution is to put the symbol in nested g elements and apply individual transforms to each of them, example.

d3 axis tick alignment

I have a d3 bar chart with, and the axis tick marks are centered below the bars (as I would expect). In this case, I would actually like to left align the axis ticks and have the ticks under the left edge of the bar. Is that possible?
EDIT: Javascript for constructing the axis
// note width is just the width of the chart
var xScale = d3.scale.ordinal().rangeRoundBands([0,width], .1);
var xAxis = d3.svg.axis().scale(xScale).orient("bottom");
here is an example of left aligning the ticks.
http://bl.ocks.org/mbostock/6186172
the idea is that after you create the ticks you collect them and aligne them:
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.attr("y", 6)
.attr("x", 6)
.style("text-anchor", "start");
Why not just create a new scale?
var padding = .1;
var axisScale = d3.scale.linear()
.domain([0, n])
.range([0 + padding, WIDTH + padding]);
Where n is the number of bars in your histogram.
I think that this should be adjusted when you add the axis to your graph.
Try (assuming your graph is called svg):
var bandSize = xScale.rangeBand();
svg.append("svg:g")
.attr("transform", "translate(-" + bandSize + "," + h + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "start")
.attr("transform", "translate(" + bandSize + ", 0)");
In my experiments, this shifts the ticks to the start of each band, and retains the text centered under each band.

Resources