I am trying to create a simple bar chart with evenly spaced bars on time scale in x-axis but unsuccessful even with the solution given here
I am getting overlaps in the bars and unevenly spaced bars. Part of my code as below:
var x = d3.time.scale().range([width/data.length/2, width-width/data.length/2]);
var y = d3.scale.linear().range([height, 0]);
bars.transition().duration(1000)
.attr("x", function(d) { return x(d.date) - Math.floor(width/data.length/2); })
.attr("width", Math.floor(width / data.length))
.attr("y", function(d) { return y(d.c); })
.attr("height", function(d) { return height - y(d.c);});
bars.enter().append("rect")
.attr("class", "air_used")
.attr("width", Math.floor(width / data.length))
.attr("x", function(d) { return x(d.date) - Math.floor((width/data.length)/2); })
.attr("y", height)
.attr("height", 0)
.transition().duration(1000)
.attr("y", function(d) { return y(d.c); })
.attr("height", function(d) { return height - y(d.c);});
JSFiddle here: http://jsfiddle.net/vivekratnavel/v7rx8ku9/2/
Any help is appreciated. Thanks!
set hours to 0hrs:0min:0sec before scaling the time on x-axis.
http://jsfiddle.net/hnj30m4j/
bars.transition().duration(1000)
.attr("x", function(d) { return x(d.date.setHours(0,0,0)) - Math.floor(width/data.length/2); })
.attr("width", Math.floor(width / data.length))
.attr("y", function(d) { return y(d.c); })
.attr("height", function(d) { return height - y(d.c);});
bars.enter().append("rect")
.attr("class", "air_used")
.attr("width", Math.floor(width / data.length))
.attr("x", function(d) { return x(d.date.setHours(0,0,0)) - Math.floor((width/data.length)/2); })
.attr("y", height)
.attr("height", 0)
.transition().duration(1000)
.attr("y", function(d) { return y(d.c); })
.attr("height", function(d) { return height - y(d.c);});
Related
I'm trying to resize a grouped bar chart with a resize() function.
function resize(){
width = parseInt(d3.select(".c_chart").style("width"), 10);
x0.rangeRound([margin.left, width-margin.right]);
x1.rangeRound([margin.left,x0.bandwidth()-margin.right])
yAxis.tickSize(width);
svg.selectAll("rect")
.attr("x", function(d) { return x1(d.key); })
.attr("width", x1.bandwidth());
svg.selectAll(".x_axis")
.call(xAxis)
.selectAll("text")
.call(wrap, x0.bandwidth());
}
When I start to resize the window, x-axis is ok but the x-position of my recent don't "follow" the ticks of my x-axis.
Then, I suspect that the problem is due to x- attribute but how can I fix that?
Here is my code: https://plnkr.co/edit/XEoM7lsBvZQmY87Wz1SP?p=preview
Add a class (gbar) to the g containing the group
svg
.append("g")
.selectAll("g")
.data(data)
.enter().append("g")
.attr("transform", function(d) { return "translate(" + x0(d.categorie) + ",0)"; })
.attr("class", "gbar")
.selectAll("rect")
.data(function(d) { return keys.map(function(key) { return {key: key, value: d[key]}; }); })
.enter().append("rect")
.attr("x", function(d) { return x1(d.key); })
.attr("y", function(d) { return y(d.value); })
.attr("width", x1.bandwidth())
.attr("height", function(d) { return height-margin.bottom - y(d.value); })
.attr("fill", function(d) { return z(d.key); });
In the resize function update the translation
svg.selectAll(".gbar")
.attr("transform", function(d) { return "translate(" + x0(d.categorie) + ",0)"; });
and update the size of the SVG
svg
.attr("width",width)
.attr("height",height);
Don't take the margin in the x1 scale
const x1 = d3.scaleBand()
.padding(0.05)
.domain(keys)
//.rangeRound([margin.left,x0.bandwidth()-margin.right])
.rangeRound([0,x0.bandwidth()]);
// resize()
//x1.rangeRound([margin.left,x0.bandwidth()-margin.right])
x1.rangeRound([0,x0.bandwidth()])
The only thing left to fix is the y-axis grid line,........
The problem:
The number of groups is dynamic, and vertical line (separator) needs dynamic padding. Group width im getting with x0.rangeBand(). Is there any way to get width of space beetween two groups dynamically?
Peace of code:
.....
var slice = svg.selectAll(".chart")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function (d) {
return "translate(" + x0(d.category) + ",0)";
});
// Create rectangles of the correct width
slice.selectAll("rect")
.data(function (d) {
return d.values;
})
.enter().append("rect")
.attr("width", x1.rangeBand())
.attr("x", function (d) {
return x1(d.rate);
})
.style("fill", function (d) {
return color(d.rate)
})
.attr("y", function (d) {
return y(0);
})
.attr("height", function (d) {
return height - y(0);
})
.on("mouseover", function (d) {
d3.select(this).style("fill", d3.rgb(color(d.rate)).darker(2));
tip.show(d);
})
.on("mouseout", function (d) {
tip.hide
d3.select(this).style("fill", color(d.rate));
})
slice.append("line")
.attr("class", "blabla")
.attr("x1", x0.rangeBand()+20)
.attr("x2", x0.rangeBand()+20)
.attr("y1", 0)
.attr("y2", height + margin.top + margin.bottom)
.style("stroke-width", 1)
.style("stroke", "#000");
.....
This is how it looks with few groups
This is how it looks with many groups
Because I see no reason why to stick to d3v3 and I can't find the d3v3 documentation easily for ordinal scales here is a d3v5 version of the code with correct placement of the vertical bars. You have to use the bandwidth and the step to calculate the position.
It is an adaptation of the example in your other question : https://bl.ocks.org/bricedev/0d95074b6d83a77dc3ad
I doubled the number of groups and it looks nice.
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x0 = d3.scaleBand()
.rangeRound([0, width])
.paddingInner(0.1);
var x1 = d3.scaleBand();
var y = d3.scaleLinear()
.range([height, 0]);
var xAxis = d3.axisBottom()
.scale(x0)
.tickSize(0);
var yAxis = d3.axisLeft()
.scale(y);
var color = d3.scaleOrdinal()
.range(["#ca0020","#f4a582","#d5d5d5","#92c5de","#0571b0"]);
var svg = d3.select('body').append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
d3.json("barchart.json", {credentials: 'same-origin'}).then(function(data) {
var categoriesNames = data.map(function(d) { return d.categorie; });
var rateNames = data[0].values.map(function(d) { return d.rate; });
x0.domain(categoriesNames);
x1.domain(rateNames).range([0, x0.bandwidth()]);
y.domain([0, d3.max(data, function(categorie) { return d3.max(categorie.values, function(d) { return d.value; }); })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.style('opacity','0')
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.style('font-weight','bold')
.text("Value");
svg.select('.y').transition().duration(500).delay(1300).style('opacity','1');
var slice = svg.selectAll(".slice")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform",function(d) { return "translate(" + x0(d.categorie) + ",0)"; });
slice.selectAll("rect")
.data(function(d) { return d.values; })
.enter().append("rect")
.attr("width", x1.bandwidth())
.attr("x", function(d) { return x1(d.rate); })
.style("fill", function(d) { return color(d.rate) })
.attr("y", function(d) { return y(0); })
.attr("height", function(d) { return height - y(0); })
.on("mouseover", function(d) {
d3.select(this).style("fill", d3.rgb(color(d.rate)).darker(2));
})
.on("mouseout", function(d) {
d3.select(this).style("fill", color(d.rate));
});
slice.selectAll("rect")
.transition()
.delay(function (d) {return Math.random()*1000;})
.duration(1000)
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); });
slice.append("line")
.attr("class", "blabla")
.attr("x1", (x0.step() - x0.bandwidth())*0.5 + x0.bandwidth())
.attr("x2", (x0.step() - x0.bandwidth())*0.5 + x0.bandwidth())
.attr("y1", 0)
.attr("y2", height + margin.top + margin.bottom)
.style("stroke-width", 1)
.style("stroke", "#000");
//Legend
var legend = svg.selectAll(".legend")
.data(data[0].values.map(function(d) { return d.rate; }).reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d,i) { return "translate(0," + i * 20 + ")"; })
.style("opacity","0");
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", function(d) { return color(d); });
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) {return d; });
legend.transition().duration(500).delay(function(d,i){ return 1300 + 100 * i; }).style("opacity","1");
});
So the trick is to recalculate padding self. V3 does not have band.step() function so I added this method to get the middle of two items:
function getMiddle(x0){
var rng = x0.range();
var band = x0.rangeBand();
var padding = 0;
if(rng.length>1){
padding = (rng[1]-rng[0] - band) *0.5;
}
return band + padding;
}
And usage:
slice.append("line")
.attr("x1", getMiddle(x0))
.attr("x2", getMiddle(x0))
.attr("y1", 0)
.attr("y2", height + margin.top + margin.bottom)
.style("stroke-width", 1)
.style("stroke", "#000");
I'm trying to learn how to code with the d3.js. I am trying to make a simple bar graph with this json file. I got stuck trying to format the xaxis in the file. I've tried looking at the d3.js API and I am still lost. I would be very grateful for any help.
Here is the result screenshot
This image is for shorter xaxis points
This output looks good
This output results when more data points in xaxis
Can anyone suggest me how to increase the xaxis length based on data point coiming to xaxis
Here is my code
.bar {
fill: #F39473;
}
.highlight {
fill: orange;
}
<!doctype html>
<html>
<head>
<script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<svg width="900" height="500"></svg>
<script>
var svg = d3.select("svg"),
top= 20, right= 20, bottom= 50, left= 70,
margin = 200,
width = svg.attr("width") - margin,
height = svg.attr("height") - margin;
var x = d3.scaleBand().range([0, width]).padding(0.4),
y = d3.scaleLinear().range([height, 0]);
var g = svg.append("g")
.attr("transform", "translate(" + 100 + "," + 100 + ")");
d3.json("data.php", function(error, data) {
data.forEach(function(d) {
d.date = (d.date);
d.count = +d.count;
})
x.domain(data.map(function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.count; })]);
g.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))
.append("text")
.attr("y", height - 250)
.attr("x", width - 100)
.attr("text-anchor", "middle")
.attr("stroke", "black")
.text("date");
g.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - margin.left)
.attr("x",0 - (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("count");
g.append("g")
.call(d3.axisLeft(y).tickFormat(function(d){
return d;
}).ticks(10))
g.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.on("mouseover", onMouseOver) //Add listener for the mouseover event
.on("mouseout", onMouseOut) //Add listener for the mouseout event
.attr("x", function(d) { return x(d.date); })
.attr("y", function(d) { return y(d.count); })
.attr("width", x.bandwidth())
.transition()
.ease(d3.easeLinear)
.duration(400)
.delay(function (d, i) {
return i * 50;
})
.attr("height", function(d) { return height - y(d.count); });
});
//mouseover event handler function
function onMouseOver(d, i) {
d3.select(this).attr('class', 'highlight');
d3.select(this)
.transition() // adds animation
.duration(400)
.attr('width', x.bandwidth() + 5)
.attr("y", function(d) { return y(d.count) - 10; })
.attr("height", function(d) { return height - y(d.count) + 10; });
g.append("text")
.attr('class', 'val')
.attr('x', function() {
return x(d.date);
})
.attr('y', function() {
return y(d.count) - 15;
})
.text(function() {
return [ +d.date, +d.count]; // Value of the text
});
}
//mouseout event handler function
function onMouseOut(d, i) {
// use the text label class to remove label on mouseout
d3.select(this).attr('class', 'bar');
d3.select(this)
.transition() // adds animation
.duration(400)
.attr('width', x.bandwidth())
.attr("y", function(d) { return y(d.count); })
.attr("height", function(d) { return height - y(d.count); });
d3.selectAll('.val')
.remove()
}
</script>
</body>
</html>
I would use d3.nest() and make a rollup out of the key value you'd want to count (d.date in this case) and use this count value in your width variable.
Here's a plunker I made using this method.
I guess the title explains the problem. Similar questions on SO refer to ordinal scales.
x axis scale:
var x = d3.time.scale()
.domain(d3.extent(data, function(d) {
return d.date;
}))
.range([width/data.length/2, width-(width/data.length/2)]);
The bars
bars.enter()
.append("rect")
.classed("column", true)
.on('mouseover', tip.show)
.on('mouseout', tip.hide)
.attr("width", (width-(width/data.length)) / (data.length+2))
.attr("x", function(d) { return x(d.date) - (width/data.length)/2; })
.attr("y", height)
.attr("height", 0)
.transition().duration(1000)
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value);});
How can I amend this code so that I have a whole and equal number of pixels between each vertical bar, and thereby getting an even spacing ?
I am using this example: http://hdnrnzk.me/2012/07/04/creating-a-bar-graph-using-d3js/
This is a link to my current code:
http://jsfiddle.net/dj8gp2hm/
var countries = ['Hong Kong', 'Singapore', 'New Zealand', 'Switzerland',
'Mauritus', 'United Arab Emirates', 'Canada', 'Australia', 'Jordan',
'Chile'],
scores = [8.98, 8.54, 8.25, 8.19, 8.05, 8.09, 8.00, 7.87, 7.86, 7.84],
chart,
width = 800,
bar_height = 40,
height = bar_height * countries.length;
chart = d3.select($("#step-1")[0])
.append('svg')
.attr('class', 'chart')
.attr('width', width)
.attr('height', height);
var x, y;
chart = d3.select($("#step-2")[0])
.append('svg')
.attr('class', 'chart')
.attr('width', width)
.attr('height', height);
x = d3.scale.linear()
.domain([0, d3.max(scores)])
.range([0, width]);
y = d3.scale.ordinal()
.domain(scores)
.rangeBands([0, height]);
chart.selectAll("rect")
.data(scores)
.enter().append("rect")
.attr("x", 0)
.attr("y", y)
.attr("width", x)
.attr("height", bar_height);
chart = d3.select($("#step-3")[0])
.append('svg')
.attr('class', 'chart')
.attr('width', width)
.attr('height', height);
chart.selectAll("rect")
.data(scores)
.enter().append("rect")
.attr("x", 0)
.attr("y", y)
.attr("width", x)
.attr("height", y.rangeBand());
chart.selectAll("text")
.data(scores)
.enter().append("text")
.attr("x", x)
.attr("y", function(d){ return y(d) + y.rangeBand()/2; } )
.attr("dx", -5)
.attr("dy", ".36em")
.attr("text-anchor", "end")
.text(String);
var left_width = 200;
chart = d3.select($("#step-4")[0])
.append('svg')
.attr('class', 'chart')
.attr('width', left_width + width)
.attr('height', height);
chart.selectAll("rect")
.data(scores)
.enter().append("rect")
.attr("x", left_width)
.attr("y", y)
.attr("width", x)
.attr("height", y.rangeBand());
chart.selectAll("text.score")
.data(scores)
.enter().append("text")
.attr("x", function(d) { return x(d) + left_width; })
.attr("y", function(d){ return y(d) + y.rangeBand()/2; } )
.attr("dx", -5)
.attr("dy", ".36em")
.attr("text-anchor", "end")
.attr('class', 'score')
.text(String);
chart.selectAll("text.name")
.data(countries)
.enter().append("text")
.attr("x", left_width / 2)
.attr("y", function(d){ return y(d) + y.rangeBand()/2; } )
.attr("dy", ".36em")
.attr("text-anchor", "middle")
.attr('class', 'name')
.text(String);
And I am having this problem:
Any help explaining why my country labels on the left aren't lining up correctly would be greatly appreciated. Thank you. :)
You have a couple of issues here.
First, you don't need to use JQuery at all, d3 does everything you need.
chart = d3.select($("#step-4")[0])
Is the same thing as:
chart = d3.select("#step-4")
As to your labels, you just have a typo in your ordinal scale. The countries are in the y direction, not the scores. (I think you might have done this to get your bars to line up right...)
y = d3.scale.ordinal()
.domain(countries) // you had scores here
.rangeBands([0, height]);
Finally, you should then use the countries index to set the y position of your bars:
chart.selectAll("rect")
.data(scores)
.enter().append("rect")
.attr("x", left_width)
.attr("y", function(d,i) { return y(countries[i]); })
.attr("width", x)
.attr("height", y.rangeBand());
chart.selectAll("text.score")
.data(scores)
.enter().append("text")
.attr("x", function(d) { return x(d) + left_width; })
.attr("y", function(d, i){ return y(countries[i]) + y.rangeBand()/2; } )