d3.js conditional tick formatting - weekdays - d3.js

So I have a problem I am using this code from bl.ocks.org, I have modified it a bit so far.
Now I am stuck with this problem: I want to format the ticks such that every Sunday appears read.
I thought this little piece of code below should do the trick for me, but apparently it does not.
I really believe it is a problem of me understanding, the call back function properly. So I would love to understand why if I call weekdays which works within the callback function, is not called by this little piece of code?
Which I have put after selectAll("text") in the big chunk of code below.
I would be very grateful if someone could help me out!
Thank you!
This is where I thought formatting should work in my code:
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(31)
.tickFormat(d3.time.format("%d-%b")) // puts date on axis in
var timeaxis = svg.append("g") // Add the X Axis
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text") //selects axis labels
.style("color", (data, function(d) { return weekdays;
if (weekdays == 0) { //red if 0 (= Sunday)
return "red";
} else {
return "black";
}
}));
.attr("dx", "-.8em")
.attr("dy", ".35em")
.attr("transform", "translate(0,15) rotate(-70)")
This is how I read in the data:
// Get the data, works!
d3.csv("monthCPU.csv", function(error, data) {
data.forEach(function(d) {
d.date = parseDate(d.date);
var weekday = d.date.getDay();
d.CPUs = +d.CPUs;
d.Memory = +d.Memory;
});
This is the dummy data I am using in a CSV file called monthCPU.csv .
date,CPUs,Memory
2012-1-1,13,70
2012-1-2,50,13
2012-1-3,80,13
2012-1-4,5,13
2012-1-5,64,100
2012-1-6,13,13
2012-1-7,64,50
2012-1-8,30,10
2012-1-9,30,10
2012-1-10,13,13
2012-1-11,13,13
2012-1-12,13,13
2012-1-13,13,13
2012-1-14,13,13
2012-1-15,13,13
2012-1-16,13,13
2012-1-17,13,13
2012-1-18,13,33
2012-1-19,60,40
2012-1-20,1,11
2012-1-21,12,12
2012-1-22,13,13
2012-1-23,13,13
2012-1-24,1,11
2012-1-25,12,12
2012-1-26,13,13
2012-1-27,13,13
2012-1-28,1,11
2012-1-29,12,12
2012-1-30,13,13
2012-1-31,13,13
2012-1-30,13,13
2012-1-31,13,13

Related

Rotate x-axis labels, D3js multi bar chart

I would like to rotate my labels on the x-axis. The labels are currently overlapping. However, I can't figure out how to alter them in the template I based the bar chart off of. My assumption is that it is somewhere in the lines below, but it is a template with a structure I am unfamiliar with as a d3 beginner. My plunker is http://plnkr.co/edit/jtGz8vtYGSHscKhrIob3?p=preview
d3.csv("data.csv", function(d, i, columns) {
for (var i = 1, n = columns.length; i < n; ++i) d[columns[i]] = +d[columns[i]];
return d;
}, function(error, data) {
if (error) throw error;
var keys = data.columns.slice(1);
x0.domain(data.map(function(d) { return d.n; }));
x1.domain(keys).rangeRound([0, x0.bandwidth()]);
y.domain([0, d3.max(data, function(d) { return d3.max(keys, function(key) { return d[key]; }); })]).nice();
var rectG = g.append("g")
.selectAll("g")
.data(data)
.enter().append("g")
.attr("transform", function(d) { return "translate(" + x0(d.n) + ",0)"; })
.selectAll("rect")
.data(function(d) { return keys.map(function(key) { return {key: key, value: d[key]}; }); })
.enter();
When appending the x-axis, you can do the following to rotate the labels:
g.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x0))
//select all text labels in the axis, then position + rotate
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-1em")
.attr("dy", "-0.5em")
.attr("transform", "rotate(-90)");
PS: this will overlap with all the text/labels you have in the bottom and you have to position them accordingly as well so things fit properly. You probably have to stretch out your svg a little bit vertically to make everything fit. I have given it a try in the plunkr; you can adjust it further if you want the graph to be larger, etc.
Forked Plunkr here - http://plnkr.co/edit/JyFdeX0wy9g0lUKi9ASC?p=preview

Replace data with new set

Using D3.js, I have something like this:
var sets = [
{ data:[{date:1980,value:10},{date:1981,value:20},{date:1982,value:30}] },
{ data:[{date:1981,value:10},{date:1982,value:20},{date:1983,value:30}] },
{ data:[{date:1982,value:10},{date:1983,value:20},{date:1984,value:30}] }
];
And I bind it to make a chart like this:
var paths = g.selectAll("path")
.data(sets);
paths.enter()
.append("path")
.datum(function(d) { return d.data; })
.attr("class","line")
.attr("d", line);
Where g is a g element inside an svg element. This works. For each item in set I get a path using the values in data. Now what I want to do is click an element and replace the data with a different set:
var altData = [
{ data:[{date:1980,value:30},{date:1981,value:20},{date:1982,value:10}] },
{ data:[{date:1981,value:10},{date:1982,value:20},{date:1983,value:30}] },
{ data:[{date:1982,value:10},{date:1983,value:20},{date:1984,value:0}] }
];
d3.select("#transition").on("click", function() {
paths.data(altData);
console.log("click");
});
But the paths.data(altData) doesn't appear to do anything. There are no console errors, but the chart doesn't change. What do I need to do to tell it that the data has changed and the lines should be redrawn? As a bonus, I'd really like this transition to be animated.
Full fiddle
Basically you need to tell d3 to redraw it. In your case, it is by calling attr("d", line).
For transition, put transition() between two attr("d", fnc). Your onclick function will look like the following
d3.select("#transition").on("click", function() {
paths.attr("d", line)
.transition()
.attr("d", function(d, i){
return line(altData[i].data)
})
});
Jsfiddle http://jsfiddle.net/8fLufc65/
Look at this plnkr that will change the data when transition is clicked.
I made the part that draws the lines into a function and pass the data for which it should be drawing the lines.
drawPaths(sets) ;
function drawPaths(sets) {
var g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var paths = g.selectAll("path")
.data(sets);
paths.enter()
.append("path")
.datum(function(d) { console.log(d); return d.data; })
.attr("class","line")
.attr("d", line);
}

d3 line chart using path is not showing anything

I am trying to draw a path that means line chart using d3.js. I am using the following code
var data;
d3.csv("myfile.csv",function(datagot){data=datagot;});
var format = d3.time.format("%Y/%m/%d %H:%M:%S");
data.forEach(function (e){
e.dist = +e.dist;
e.speed = +e.speed;
e.lat=+e.lat;
e.lon=+e.lon;
e.dd=format.parse(e.time);
});
var xScale = d3.time.scale().range([margin.left, width - margin.right]).domain([d3.min(dataset,function(d){return d.dd}),d3.max(dataset,function(d){ return d.dd})]),
yScale = d3.scale.linear().range([height - margin.top, margin.bottom]).domain([d3.min(dataset,function(d){return d.dist;}),d3.max(dataset,function(d){return d.dist;})]),
xAxis = d3.svg.axis()
.scale(xScale).ticks(10).tickFormat(d3.time.format("%H:%M")).tickPadding(2),
yAxis = d3.svg.axis()
.scale(yScale).orient("left").tickPadding(5).ticks(5);
var svg = d3.select("body").append("svg")
.attr("width", width-50 )
.attr("height", height -60);
svg.append("svg:g")
.attr("class","axis")
.attr("transform", "translate(0," + (height - margin.bottom) + ")")
.call(xAxis);
// x axis label
svg.append("text")
.attr("x", width / 2 )
.attr("y", height - 60)
.style("text-anchor", "middle")
.text("Time");
svg.append("svg:g")
.attr("class","axis")
.attr("transform", "translate(" + (margin.left-10) + ",-90)")
.call(yAxis);
// Y axis label
svg.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 15)
.attr("x",70- (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Distance");
// svg.append("g")
//.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var x = d3.scale.linear()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
lineh = d3.svg.line().x(function(d) {
return x(d.dd);
}).y(function(d) {
return y(d.dist);
});
var line = svg.append("g").attr("transform", "translate(" + margin.left + "," + (-margin.top) + ")").selectAll(".hour")
.data(outputf)
.enter().append("path")
//.attr("x", function(d) { return (d.dd.getMinutes())*15 ; })
// .attr("cx", function(d) { return (d.dd.getMinutes())*10 ; })
//.attr("y", function(d) { return (d.dist)*50 ; })
// .attr("cy", function(d) { return height-100-(d.dist)*50 ; })
//.attr("r",3)
/* .attr("rx", 2)
.attr("ry", 2)*/
.attr("d",lineh)
.attr("class", "line");
But its actually not plotting anything. I am giving the jsfiddle for your help to understand. http://jsfiddle.net/1b0gn0r2/. The jsfiddle is not well organized but it contains my code and the csv data I am using is at the bottom. Can anyone help me to find the error?
In my actual code the output is the following
There's a lot that seems to be wrong in your code! Having various things missing (like margin, height, width, other variables, the CSS etc), makes it very painful to answer your question.
To start with, if you're struggling with jsfiddle you may prefer Plunker, which makes it easier to organise your code and data.
I've created a plunk that works with your code here: http://plnkr.co/edit/ZEi7U6qQ8pxq06FdDIW6?p=preview
...but it involved quite a few changes, which I'll try to summarise:
Loading is asynchronous, so this line:
d3.csv("myfile.csv",function(datagot){data=datagot;});
will not do what you expect. The rest of the code will go off and execute (eg drawing axes) without data having been properly set. So you try to draw the line before the data is ready. Fix this by inserting the main code into the data load function.
Defining scales. You seem to define 4 different scales:
xScale = d3.time.scale()
yScale = d3.scale.linear()
x = d3.scale.linear()
y = d3.scale.linear()
That causes problems later in your line drawing function (more to come on that). I've got rid of x and y here
The line drawing function lineh expects the scales to be x and y, but I suggest using the xScale and yScale ones you've defined before.
// line function
lineh = d3.svg.line()
.x(function(d) {
return xScale(d.dd); // <- do not use d(d.dd)
})...
Calling the line function should be done in a completely different way: Your code:
var line = svg.append("g")
.data(outputf)
.enter().append("path")
.attr("d",lineh)
.attr("class", "line");
Not sure what outputf is so I ignored that. lineh (defines how to draw the line) should be called with the data. I changed the call to:
var line = svg.append("g")
.append("path")
.attr("d", lineh(dataset)) // <- Note the change
.attr("class", "line");
Finally I reorganised the code to bring some variable definitions to the top of the file and make it easier to follow.

d3.js: Updating a pie with lables

I'm trying to set up a pie on d3.js that will constantly update its data (slices) and change the labels.
The slices of data may vary and are not constant (could be a 2 slices pie and 5 slices, etc.)
I've reviewed several of examples such as:
http://jsfiddle.net/emepyc/VYud2/3/
http://bl.ocks.org/mbostock/3808218
But the thing is, my implementation is quite different since I use both text lables and paths.
The problem: data is not updated and text is not removed.
Here is my code: http://jsfiddle.net/Kitt0s/vfkSs/
function tests (data) {
data = data ? data : { "slice1": Math.floor((Math.random()*10)+1), "slice2": Math.floor((Math.random()*10)+1), "slice3": Math.floor((Math.random()*10)+1), "slice4": Math.floor((Math.random()*10)+1) };
var dataa = d3.entries(data);
var cv_path = cv_svg.selectAll("g.slice")
.data(cv_pie(dataa));
cv_path.enter()
.append("g")
.attr("class", "slice")
.append("path")
.attr("fill", function(d, i) { return cv_color(i); } )
.attr("d", cv_arc)
.each(function(d) { this._current = d; });
cv_path.transition().duration(750).attrTween("d", cv_arcTween);
cv_path.exit().remove();
var cv_text = d3.selectAll(".slice")
.append("text")
.attr("transform", function(d) {
d.innerRadius = 0;
d.outerRadius = cv_r;
return "translate(" + cv_arc.centroid(d) + ")";
})
.attr("text-anchor", "middle")
.attr("font-weight", "bold")
.attr("fill", "#FFFFFF")
.attr("font-size", "30px")
.text(function(d) { return d.data.key + "(" + d.data.value + ")"; });
cv_text.exit().remove();
}
I've spent an awful lot of time trying to get this right, but still seem to get stuck (every time something else breaks.. :P)
Your help would be highly appreciated!
You were almost there. The key is to treat the text exactly the same as the paths and handle the .exit() selection by removing it. I've modified your jsfiddle here. I've removed the g elements to make it a bit easier to understand.

Getting text area using getBBox()

var text = vis.selectAll("text")
.data(words, function(d) { return d.text.toLowerCase(); });
text.enter().append("text")
.attr("text-anchor", "middle")
.attr("transform", function(d) {
return "translate(" + [d.x, d.y] + ")"})
.style("font-size", function(d) { return d.size + "px"; })
var bbox = text.node().getBBox();
How do I use getBBox() to get the text area of each text?
The best approach here depends on what you're trying to do. Most d3 callback functions will supply the current DOM element as this, so this should work:
text.each(function() {
console.log(this.getBBox());
});
Beyond that, the question is the context in which you need to use that number. For example, to get the sum of the text widths, you could do:
var textWidth = 0;
text.each(function() {
textWidth += this.getBBox().width;
});
You can also do this synchronously on an element by using node():
console.log(text.node().getBBox());

Resources